chore: move server-side code to src/server (#411)

This commit is contained in:
Dmitry Gozman 2020-01-07 15:27:45 -08:00 committed by Pavel Feldman
parent e0693654b7
commit 6318b1de75
16 changed files with 232 additions and 194 deletions

1
index.d.ts vendored
View file

@ -18,3 +18,4 @@ export * from './lib/api';
export function playwright(browser: 'chromium'): import('./lib/api').ChromiumPlaywright; export function playwright(browser: 'chromium'): import('./lib/api').ChromiumPlaywright;
export function playwright(browser: 'firefox'): import('./lib/api').FirefoxPlaywright; export function playwright(browser: 'firefox'): import('./lib/api').FirefoxPlaywright;
export function playwright(browser: 'webkit'): import('./lib/api').WebKitPlaywright; export function playwright(browser: 'webkit'): import('./lib/api').WebKitPlaywright;
export function connect(browser: 'chromium'): import('./lib/api').ChromiumBrowser.connect;

View file

@ -33,3 +33,9 @@ module.exports.playwright = browser => {
return new api.WebKitPlaywright(__dirname, packageJson.playwright.webkit_revision); return new api.WebKitPlaywright(__dirname, packageJson.playwright.webkit_revision);
throw new Error(`Unsupported browser "${browser}"`); throw new Error(`Unsupported browser "${browser}"`);
}; };
module.exports.connect = browser => {
if (browser === 'chromium')
return api.ChromiumBrowser.connect;
throw new Error(`Unsupported browser "${browser}"`);
};

View file

@ -17,7 +17,6 @@
export { Accessibility } from './accessibility'; export { Accessibility } from './accessibility';
export { Browser, BrowserServer } from './browser'; export { Browser, BrowserServer } from './browser';
export { BrowserContext } from './browserContext'; export { BrowserContext } from './browserContext';
export { BrowserFetcher } from './browserFetcher';
export { ConsoleMessage } from './console'; export { ConsoleMessage } from './console';
export { Dialog } from './dialog'; export { Dialog } from './dialog';
export { ElementHandle } from './dom'; export { ElementHandle } from './dom';
@ -28,6 +27,9 @@ export { JSHandle } from './javascript';
export { Request, Response } from './network'; export { Request, Response } from './network';
export { Coverage, FileChooser, Page, Worker } from './page'; export { Coverage, FileChooser, Page, Worker } from './page';
export { BrowserFetcher } from './server/browserFetcher';
export { CRPlaywright as ChromiumPlaywright, CRBrowserServer as ChromiumBrowserServer } from './server/crPlaywright';
export * from './chromium/crApi'; export * from './chromium/crApi';
export * from './firefox/ffApi'; export * from './firefox/ffApi';
export * from './webkit/wkApi'; export * from './webkit/wkApi';

View file

@ -16,5 +16,4 @@
export { CRBrowser as ChromiumBrowser } from './crBrowser'; export { CRBrowser as ChromiumBrowser } from './crBrowser';
export { CRSession as ChromiumSession } from './crConnection'; export { CRSession as ChromiumSession } from './crConnection';
export { CRPlaywright as ChromiumPlaywright } from './crPlaywright';
export { CRTarget as ChromiumTarget } from './crTarget'; export { CRTarget as ChromiumTarget } from './crTarget';

View file

@ -28,9 +28,16 @@ import * as browser from '../browser';
import * as network from '../network'; import * as network from '../network';
import * as types from '../types'; import * as types from '../types';
import * as platform from '../platform'; import * as platform from '../platform';
import { ConnectionTransport } from '../transport'; import { ConnectionTransport, SlowMoTransport } from '../transport';
import { readProtocolStream } from './crProtocolHelper'; import { readProtocolStream } from './crProtocolHelper';
export type CRConnectOptions = {
slowMo?: number,
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
};
export class CRBrowser extends browser.Browser { export class CRBrowser extends browser.Browser {
_connection: CRConnection; _connection: CRConnection;
_client: CRSession; _client: CRSession;
@ -42,10 +49,9 @@ export class CRBrowser extends browser.Browser {
private _tracingPath = ''; private _tracingPath = '';
private _tracingClient: CRSession | undefined; private _tracingClient: CRSession | undefined;
static async create( static async connect(options: CRConnectOptions): Promise<CRBrowser> {
transport: ConnectionTransport) { const transport = await createTransport(options);
const connection = new CRConnection(transport); const connection = new CRConnection(transport);
const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts'); const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts');
const browser = new CRBrowser(connection, browserContextIds); const browser = new CRBrowser(connection, browserContextIds);
await connection.rootSession.send('Target.setDiscoverTargets', { discover: true }); await connection.rootSession.send('Target.setDiscoverTargets', { discover: true });
@ -297,3 +303,25 @@ export class CRBrowser extends browser.Browser {
return !this._connection._closed; return !this._connection._closed;
} }
} }
export async function createTransport(options: CRConnectOptions): Promise<ConnectionTransport> {
assert(Number(!!options.browserWSEndpoint) + Number(!!options.browserURL) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to playwright.connect');
let transport: ConnectionTransport | undefined;
let connectionURL: string = '';
if (options.transport) {
transport = options.transport;
} else if (options.browserWSEndpoint) {
connectionURL = options.browserWSEndpoint;
transport = await platform.createWebSocketTransport(options.browserWSEndpoint);
} else if (options.browserURL) {
try {
const data = await platform.fetchUrl(new URL('/json/version', options.browserURL).href);
connectionURL = JSON.parse(data).webSocketDebuggerUrl;
} catch (e) {
e.message = `Failed to fetch browser webSocket url from ${options.browserURL}: ` + e.message;
throw e;
}
transport = await platform.createWebSocketTransport(connectionURL);
}
return SlowMoTransport.wrap(transport, options.slowMo);
}

View file

@ -18,14 +18,15 @@
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import { FFBrowser } from './ffBrowser'; import { FFBrowser } from './ffBrowser';
import { BrowserFetcher, BrowserFetcherOptions } from '../browserFetcher'; import { BrowserFetcher, BrowserFetcherOptions } from '../server/browserFetcher';
import * as fs from 'fs'; import * as fs from 'fs';
import * as util from 'util'; import * as util from 'util';
import { assert } from '../helper'; import { assert } from '../helper';
import { TimeoutError } from '../errors'; import { TimeoutError } from '../errors';
import { WebSocketTransport, SlowMoTransport } from '../transport'; import { SlowMoTransport } from '../transport';
import { launchProcess, waitForLine } from '../processLauncher'; import { launchProcess, waitForLine } from '../server/processLauncher';
import { BrowserServer } from '../browser'; import { BrowserServer } from '../browser';
import * as platform from '../platform';
const mkdtempAsync = util.promisify(fs.mkdtemp); const mkdtempAsync = util.promisify(fs.mkdtemp);
const writeFileAsync = util.promisify(fs.writeFile); const writeFileAsync = util.promisify(fs.writeFile);
@ -122,7 +123,7 @@ export class FFLauncher {
const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Firefox!`); const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Firefox!`);
const match = await waitForLine(launchedProcess, launchedProcess.stdout, /^Juggler listening on (ws:\/\/.*)$/, timeout, timeoutError); const match = await waitForLine(launchedProcess, launchedProcess.stdout, /^Juggler listening on (ws:\/\/.*)$/, timeout, timeoutError);
const url = match[1]; const url = match[1];
const transport = await WebSocketTransport.create(url); const transport = await platform.createWebSocketTransport(url);
browser = await FFBrowser.create(SlowMoTransport.wrap(transport, slowMo)); browser = await FFBrowser.create(SlowMoTransport.wrap(transport, slowMo));
await browser._waitForTarget(t => t.type() === 'page'); await browser._waitForTarget(t => t.type() === 'page');
return new BrowserServer(browser, launchedProcess, url); return new BrowserServer(browser, launchedProcess, url);

View file

@ -17,12 +17,13 @@
import * as browsers from '../browser'; import * as browsers from '../browser';
import { FFBrowser } from './ffBrowser'; import { FFBrowser } from './ffBrowser';
import { BrowserFetcher, BrowserFetcherOptions, OnProgressCallback, BrowserFetcherRevisionInfo } from '../browserFetcher'; import { BrowserFetcher, BrowserFetcherOptions, OnProgressCallback, BrowserFetcherRevisionInfo } from '../server/browserFetcher';
import { WebSocketTransport, SlowMoTransport } from '../transport'; import { SlowMoTransport } from '../transport';
import { DeviceDescriptors } from '../deviceDescriptors'; import { DeviceDescriptors } from '../deviceDescriptors';
import * as Errors from '../errors'; import * as Errors from '../errors';
import * as types from '../types'; import * as types from '../types';
import { FFLauncher, createBrowserFetcher } from './ffLauncher'; import { FFLauncher, createBrowserFetcher } from './ffLauncher';
import * as platform from '../platform';
export class FFPlaywright { export class FFPlaywright {
private _projectRoot: string; private _projectRoot: string;
@ -52,7 +53,7 @@ export class FFPlaywright {
} }
async connect(options: { slowMo?: number, browserWSEndpoint: string }): Promise<FFBrowser> { async connect(options: { slowMo?: number, browserWSEndpoint: string }): Promise<FFBrowser> {
const transport = await WebSocketTransport.create(options.browserWSEndpoint); const transport = await platform.createWebSocketTransport(options.browserWSEndpoint);
return FFBrowser.create(SlowMoTransport.wrap(transport, options.slowMo || 0)); return FFBrowser.create(SlowMoTransport.wrap(transport, options.slowMo || 0));
} }

View file

@ -9,9 +9,13 @@ import * as nodeBuffer from 'buffer';
import * as mime from 'mime'; import * as mime from 'mime';
import * as jpeg from 'jpeg-js'; import * as jpeg from 'jpeg-js';
import * as png from 'pngjs'; import * as png from 'pngjs';
import * as http from 'http';
import * as https from 'https';
import * as NodeWebSocket from 'ws';
import { assert, helper } from './helper'; import { assert, helper } from './helper';
import * as types from './types'; import * as types from './types';
import { ConnectionTransport } from './transport';
export const isNode = typeof process === 'object' && !!process && typeof process.versions === 'object' && !!process.versions && !!process.versions.node; export const isNode = typeof process === 'object' && !!process && typeof process.versions === 'object' && !!process.versions && !!process.versions.node;
@ -219,3 +223,79 @@ export function pngToJpeg(buffer: Buffer): Buffer {
assert(isNode, 'Converting from png to jpeg is only supported in Node.js'); assert(isNode, 'Converting from png to jpeg is only supported in Node.js');
return jpeg.encode(png.PNG.sync.read(buffer)).data; return jpeg.encode(png.PNG.sync.read(buffer)).data;
} }
function nodeFetch(url: string): Promise<string> {
let resolve: (url: string) => void;
let reject: (e: Error) => void;
const promise = new Promise<string>((res, rej) => { resolve = res; reject = rej; });
const endpointURL = new URL(url);
const protocol = endpointURL.protocol === 'https:' ? https : http;
const request = protocol.request(endpointURL, res => {
let data = '';
if (res.statusCode !== 200) {
// Consume response data to free up memory.
res.resume();
reject(new Error('HTTP ' + res.statusCode));
return;
}
res.setEncoding('utf8');
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(data));
});
request.on('error', reject);
request.end();
return promise;
}
export function fetchUrl(url: string): Promise<string> {
if (isNode)
return nodeFetch(url);
return fetch(url).then(response => {
if (!response.ok)
throw new Error('HTTP ' + response.status + ' ' + response.statusText);
return response.text();
});
}
class WebSocketTransport implements ConnectionTransport {
private _ws: WebSocket;
onmessage?: (message: string) => void;
onclose?: () => void;
constructor(ws: WebSocket) {
this._ws = ws;
this._ws.addEventListener('message', event => {
if (this.onmessage)
this.onmessage.call(null, event.data);
});
this._ws.addEventListener('close', event => {
if (this.onclose)
this.onclose.call(null);
});
// Silently ignore all errors - we don't know what to do with them.
this._ws.addEventListener('error', () => {});
}
send(message: string) {
this._ws.send(message);
}
close() {
this._ws.close();
}
}
export function createWebSocketTransport(url: string): Promise<ConnectionTransport> {
return new Promise((resolve, reject) => {
const ws = (isNode ? new NodeWebSocket(url, [], {
perMessageDeflate: false,
maxPayload: 256 * 1024 * 1024, // 256Mb
}) : new WebSocket(url)) as WebSocket;
ws.addEventListener('open', () => resolve(new WebSocketTransport(ws)));
ws.addEventListener('error', reject);
});
}

View file

@ -19,21 +19,20 @@ import * as extract from 'extract-zip';
import * as fs from 'fs'; import * as fs from 'fs';
import * as ProxyAgent from 'https-proxy-agent'; import * as ProxyAgent from 'https-proxy-agent';
import * as path from 'path'; import * as path from 'path';
import * as platform from './platform'; import * as platform from '../platform';
// @ts-ignore
import { getProxyForUrl } from 'proxy-from-env'; import { getProxyForUrl } from 'proxy-from-env';
import * as removeRecursive from 'rimraf'; import * as removeRecursive from 'rimraf';
import * as URL from 'url'; import * as URL from 'url';
import { assert } from './helper'; import { assert } from '../helper';
const readdirAsync = platform.promisify(fs.readdir.bind(fs)); const readdirAsync = platform.promisify(fs.readdir.bind(fs));
const mkdirAsync = platform.promisify(fs.mkdir.bind(fs)); const mkdirAsync = platform.promisify(fs.mkdir.bind(fs));
const unlinkAsync = platform.promisify(fs.unlink.bind(fs)); const unlinkAsync = platform.promisify(fs.unlink.bind(fs));
const chmodAsync = platform.promisify(fs.chmod.bind(fs)); const chmodAsync = platform.promisify(fs.chmod.bind(fs));
function existsAsync(filePath) { function existsAsync(filePath: string): Promise<boolean> {
let fulfill = null; let fulfill: (exists: boolean) => void;
const promise = new Promise(x => fulfill = x); const promise = new Promise<boolean>(x => fulfill = x);
fs.access(filePath, err => fulfill(!err)); fs.access(filePath, err => fulfill(!err));
return promise; return promise;
} }

View file

@ -15,25 +15,22 @@
* limitations under the License. * limitations under the License.
*/ */
import * as http from 'http';
import * as https from 'https';
import * as URL from 'url';
import * as fs from 'fs'; import * as fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import * as util from 'util'; import * as util from 'util';
import { BrowserFetcher, BrowserFetcherOptions, BrowserFetcherRevisionInfo, OnProgressCallback } from '../browserFetcher'; import { BrowserFetcher, BrowserFetcherOptions, BrowserFetcherRevisionInfo, OnProgressCallback } from '../server/browserFetcher';
import { DeviceDescriptors } from '../deviceDescriptors'; import { DeviceDescriptors } from '../deviceDescriptors';
import * as Errors from '../errors'; import * as Errors from '../errors';
import * as types from '../types'; import * as types from '../types';
import { assert } from '../helper'; import { assert } from '../helper';
import { ConnectionTransport, WebSocketTransport, SlowMoTransport, PipeTransport } from '../transport'; import { CRBrowser, CRConnectOptions, createTransport } from '../chromium/crBrowser';
import { CRBrowser } from './crBrowser';
import * as platform from '../platform'; import * as platform from '../platform';
import { TimeoutError } from '../errors'; import { TimeoutError } from '../errors';
import { launchProcess, waitForLine } from '../processLauncher'; import { launchProcess, waitForLine } from '../server/processLauncher';
import { ChildProcess } from 'child_process'; import { ChildProcess } from 'child_process';
import { CRConnection } from './crConnection'; import { CRConnection } from '../chromium/crConnection';
import { PipeTransport } from './pipeTransport';
export type SlowMoOptions = { export type SlowMoOptions = {
slowMo?: number, slowMo?: number,
@ -58,24 +55,17 @@ export type LaunchOptions = ChromeArgOptions & SlowMoOptions & {
pipe?: boolean, pipe?: boolean,
}; };
export type ConnectOptions = SlowMoOptions & {
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
};
export class CRBrowserServer { export class CRBrowserServer {
private _process: ChildProcess; private _process: ChildProcess;
private _connectOptions: ConnectOptions; private _connectOptions: CRConnectOptions;
constructor(process: ChildProcess, connectOptions: ConnectOptions) { constructor(process: ChildProcess, connectOptions: CRConnectOptions) {
this._process = process; this._process = process;
this._connectOptions = connectOptions; this._connectOptions = connectOptions;
} }
async connect(): Promise<CRBrowser> { async connect(): Promise<CRBrowser> {
const transport = await createTransport(this._connectOptions); return CRBrowser.connect(this._connectOptions);
return CRBrowser.create(transport);
} }
process(): ChildProcess { process(): ChildProcess {
@ -86,7 +76,7 @@ export class CRBrowserServer {
return this._connectOptions.browserWSEndpoint || null; return this._connectOptions.browserWSEndpoint || null;
} }
connectOptions(): ConnectOptions { connectOptions(): CRConnectOptions {
return this._connectOptions; return this._connectOptions;
} }
@ -179,7 +169,7 @@ export class CRPlaywright {
let server: CRBrowserServer | undefined; let server: CRBrowserServer | undefined;
try { try {
let connectOptions: ConnectOptions | undefined; let connectOptions: CRConnectOptions | undefined;
let browserWSEndpoint: string = ''; let browserWSEndpoint: string = '';
if (!usePipe) { if (!usePipe) {
const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${this._revision}`); const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${this._revision}`);
@ -199,9 +189,8 @@ export class CRPlaywright {
} }
} }
async connect(options: ConnectOptions): Promise<CRBrowser> { async connect(options: CRConnectOptions): Promise<CRBrowser> {
const transport = await createTransport(options); return CRBrowser.connect(options);
return CRBrowser.create(transport);
} }
executablePath(): string { executablePath(): string {
@ -328,49 +317,3 @@ const DEFAULT_ARGS = [
'--password-store=basic', '--password-store=basic',
'--use-mock-keychain', '--use-mock-keychain',
]; ];
function getWSEndpoint(browserURL: string): Promise<string> {
let resolve: (url: string) => void;
let reject: (e: Error) => void;
const promise = new Promise<string>((res, rej) => { resolve = res; reject = rej; });
const endpointURL = URL.resolve(browserURL, '/json/version');
const protocol = endpointURL.startsWith('https') ? https : http;
const requestOptions = Object.assign(URL.parse(endpointURL), { method: 'GET' });
const request = protocol.request(requestOptions, res => {
let data = '';
if (res.statusCode !== 200) {
// Consume response data to free up memory.
res.resume();
reject(new Error('HTTP ' + res.statusCode));
return;
}
res.setEncoding('utf8');
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(JSON.parse(data).webSocketDebuggerUrl));
});
request.on('error', reject);
request.end();
return promise.catch(e => {
e.message = `Failed to fetch browser webSocket url from ${endpointURL}: ` + e.message;
throw e;
});
}
async function createTransport(options: ConnectOptions): Promise<ConnectionTransport> {
assert(Number(!!options.browserWSEndpoint) + Number(!!options.browserURL) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to playwright.connect');
let transport: ConnectionTransport | undefined;
let connectionURL: string = '';
if (options.transport) {
transport = options.transport;
} else if (options.browserWSEndpoint) {
connectionURL = options.browserWSEndpoint;
transport = await WebSocketTransport.create(options.browserWSEndpoint);
} else if (options.browserURL) {
connectionURL = await getWSEndpoint(options.browserURL);
transport = await WebSocketTransport.create(connectionURL);
}
return SlowMoTransport.wrap(transport, options.slowMo);
}

View file

@ -0,0 +1,73 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { debugError, helper, RegisteredListener } from '../helper';
import { ConnectionTransport } from '../transport';
export class PipeTransport implements ConnectionTransport {
private _pipeWrite: NodeJS.WritableStream;
private _pendingMessage = '';
private _eventListeners: RegisteredListener[];
onmessage?: (message: string) => void;
onclose?: () => void;
constructor(pipeWrite: NodeJS.WritableStream, pipeRead: NodeJS.ReadableStream) {
this._pipeWrite = pipeWrite;
this._eventListeners = [
helper.addEventListener(pipeRead, 'data', buffer => this._dispatch(buffer)),
helper.addEventListener(pipeRead, 'close', () => {
if (this.onclose)
this.onclose.call(null);
}),
helper.addEventListener(pipeRead, 'error', debugError),
helper.addEventListener(pipeWrite, 'error', debugError),
];
this.onmessage = null;
this.onclose = null;
}
send(message: string) {
this._pipeWrite.write(message);
this._pipeWrite.write('\0');
}
_dispatch(buffer: Buffer) {
let end = buffer.indexOf('\0');
if (end === -1) {
this._pendingMessage += buffer.toString();
return;
}
const message = this._pendingMessage + buffer.toString(undefined, 0, end);
if (this.onmessage)
this.onmessage.call(null, message);
let start = end + 1;
end = buffer.indexOf('\0', start);
while (end !== -1) {
if (this.onmessage)
this.onmessage.call(null, buffer.toString(undefined, start, end));
start = end + 1;
end = buffer.indexOf('\0', start);
}
this._pendingMessage = buffer.toString(undefined, start);
}
close() {
this._pipeWrite = null;
helper.removeEventListeners(this._eventListeners);
}
}

View file

@ -18,10 +18,10 @@
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
import * as stream from 'stream'; import * as stream from 'stream';
import * as removeFolder from 'rimraf'; import * as removeFolder from 'rimraf';
import { helper } from './helper'; import { helper } from '../helper';
import * as readline from 'readline'; import * as readline from 'readline';
import { TimeoutError } from './errors'; import { TimeoutError } from '../errors';
import * as platform from './platform'; import * as platform from '../platform';
const removeFolderAsync = platform.promisify(removeFolder); const removeFolderAsync = platform.promisify(removeFolder);

View file

@ -15,9 +15,6 @@
* limitations under the License. * limitations under the License.
*/ */
import * as WebSocket from 'ws';
import { debugError, helper, RegisteredListener } from './helper';
export interface ConnectionTransport { export interface ConnectionTransport {
send(s: string): void; send(s: string): void;
close(): void; close(): void;
@ -25,100 +22,6 @@ export interface ConnectionTransport {
onclose?: () => void, onclose?: () => void,
} }
export class WebSocketTransport implements ConnectionTransport {
private _ws: WebSocket;
onmessage?: (message: string) => void;
onclose?: () => void;
static create(url: string): Promise<WebSocketTransport> {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url, [], {
perMessageDeflate: false,
maxPayload: 256 * 1024 * 1024, // 256Mb
});
ws.addEventListener('open', () => resolve(new WebSocketTransport(ws, url)));
ws.addEventListener('error', reject);
});
}
constructor(ws: WebSocket, url: string) {
this._ws = ws;
this._ws.addEventListener('message', event => {
if (this.onmessage)
this.onmessage.call(null, event.data);
});
this._ws.addEventListener('close', event => {
if (this.onclose)
this.onclose.call(null);
});
// Silently ignore all errors - we don't know what to do with them.
this._ws.addEventListener('error', () => {});
}
send(message: string) {
this._ws.send(message);
}
close() {
this._ws.close();
}
}
export class PipeTransport implements ConnectionTransport {
private _pipeWrite: NodeJS.WritableStream;
private _pendingMessage = '';
private _eventListeners: RegisteredListener[];
onmessage?: (message: string) => void;
onclose?: () => void;
constructor(pipeWrite: NodeJS.WritableStream, pipeRead: NodeJS.ReadableStream) {
this._pipeWrite = pipeWrite;
this._eventListeners = [
helper.addEventListener(pipeRead, 'data', buffer => this._dispatch(buffer)),
helper.addEventListener(pipeRead, 'close', () => {
if (this.onclose)
this.onclose.call(null);
}),
helper.addEventListener(pipeRead, 'error', debugError),
helper.addEventListener(pipeWrite, 'error', debugError),
];
this.onmessage = null;
this.onclose = null;
}
send(message: string) {
this._pipeWrite.write(message);
this._pipeWrite.write('\0');
}
_dispatch(buffer: Buffer) {
let end = buffer.indexOf('\0');
if (end === -1) {
this._pendingMessage += buffer.toString();
return;
}
const message = this._pendingMessage + buffer.toString(undefined, 0, end);
if (this.onmessage)
this.onmessage.call(null, message);
let start = end + 1;
end = buffer.indexOf('\0', start);
while (end !== -1) {
if (this.onmessage)
this.onmessage.call(null, buffer.toString(undefined, start, end));
start = end + 1;
end = buffer.indexOf('\0', start);
}
this._pendingMessage = buffer.toString(undefined, start);
}
close() {
this._pipeWrite = null;
helper.removeEventListeners(this._eventListeners);
}
}
export class SlowMoTransport { export class SlowMoTransport {
private readonly _delay: number; private readonly _delay: number;
private readonly _delegate: ConnectionTransport; private readonly _delegate: ConnectionTransport;

View file

@ -17,14 +17,15 @@
import { assert } from '../helper'; import { assert } from '../helper';
import { WKBrowser } from './wkBrowser'; import { WKBrowser } from './wkBrowser';
import { BrowserFetcher, BrowserFetcherOptions } from '../browserFetcher'; import { BrowserFetcher, BrowserFetcherOptions } from '../server/browserFetcher';
import { PipeTransport, SlowMoTransport } from '../transport'; import { SlowMoTransport } from '../transport';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import * as path from 'path'; import * as path from 'path';
import * as util from 'util'; import * as util from 'util';
import * as os from 'os'; import * as os from 'os';
import { launchProcess } from '../processLauncher'; import { launchProcess } from '../server/processLauncher';
import { BrowserServer } from '../browser'; import { BrowserServer } from '../browser';
import { PipeTransport } from '../server/pipeTransport';
const DEFAULT_ARGS = [ const DEFAULT_ARGS = [
]; ];

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import * as browsers from '../browser'; import * as browsers from '../browser';
import { BrowserFetcher, BrowserFetcherOptions, OnProgressCallback, BrowserFetcherRevisionInfo } from '../browserFetcher'; import { BrowserFetcher, BrowserFetcherOptions, OnProgressCallback, BrowserFetcherRevisionInfo } from '../server/browserFetcher';
import { DeviceDescriptors } from '../deviceDescriptors'; import { DeviceDescriptors } from '../deviceDescriptors';
import * as Errors from '../errors'; import * as Errors from '../errors';
import * as types from '../types'; import * as types from '../types';

View file

@ -98,11 +98,12 @@ function checkSources(sources) {
excludeClasses.add(className); excludeClasses.add(className);
} }
} }
if (!node.getSourceFile().fileName.endsWith('platform.ts')) { const fileName = node.getSourceFile().fileName;
if (!fileName.endsWith('platform.ts') && !fileName.includes('src/server/')) {
// Only relative imports. // Only relative imports.
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) { if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
const module = node.moduleSpecifier.text; const module = node.moduleSpecifier.text;
if (!module.startsWith('.')) { if (!module.startsWith('.') || path.resolve(path.dirname(fileName), module).includes('src/server')) {
const lac = ts.getLineAndCharacterOfPosition(node.getSourceFile(), node.moduleSpecifier.pos); const lac = ts.getLineAndCharacterOfPosition(node.getSourceFile(), node.moduleSpecifier.pos);
errors.push(`Disallowed import "${module}" at ${node.getSourceFile().fileName}:${lac.line + 1}`); errors.push(`Disallowed import "${module}" at ${node.getSourceFile().fileName}:${lac.line + 1}`);
} }