Skip to content

Commit

Permalink
fix: use stdio for CDP instead of TCP (cypress-io#14348)
Browse files Browse the repository at this point in the history
  • Loading branch information
flotwig authored and pashidlos committed Jan 30, 2021
1 parent d0b5dbb commit acc63c4
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 100 deletions.
11 changes: 10 additions & 1 deletion packages/launcher/lib/browsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function launch (
browser: FoundBrowser,
url: string,
args: string[] = [],
opts: { pipeStdio?: boolean } = {},
) {
log('launching browser %o', { browser, url })

Expand All @@ -110,7 +111,15 @@ export function launch (

log('spawning browser with args %o', { args })

const proc = cp.spawn(browser.path, args, { stdio: ['ignore', 'pipe', 'pipe'] })
const stdio = ['ignore', 'pipe', 'pipe']

if (opts.pipeStdio) {
// also pipe stdio 3 and 4 for access to debugger protocol
stdio.push('pipe', 'pipe')
}

// @ts-ignore
const proc = cp.spawn(browser.path, args, { stdio })

proc.stdout.on('data', (buf) => {
log('%s stdout: %s', browser.name, String(buf).trim())
Expand Down
67 changes: 65 additions & 2 deletions packages/server/__snapshots__/5_cdp_spec.ts.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
exports['e2e cdp / handles disconnections as expected'] = `
exports['e2e cdp / with TCP transport / handles disconnections as expected'] = `
====================================================================================================
Expand Down Expand Up @@ -59,4 +59,67 @@ Error: connect ECONNREFUSED 127.0.0.1:7777
✖ 1 of 1 failed (100%) XX:XX - - 1 - -
`
`

exports['e2e cdp / with stdio transport / falls back to connecting via tcp when stdio cannot be connected'] = `
====================================================================================================
(Run Starting)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 1 found (spec.ts) │
│ Searched: cypress/integration/spec.ts │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: spec.ts (1 of 1)
Warning: Cypress failed to connect to Chrome via stdio after 1 second. Falling back to TCP...
Connecting to Chrome via TCP was successful, continuing with tests.
passes
✓ passes
1 passing
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1 │
│ Passing: 1 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: spec.ts │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /XXX/XXX/XXX/cypress/videos/spec.ts.mp4 (X second)
====================================================================================================
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ spec.ts XX:XX 1 1 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! XX:XX 1 1 - - -
`
62 changes: 49 additions & 13 deletions packages/server/lib/browsers/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as CriClient from './cri-client'
import * as protocol from './protocol'
import utils from './utils'
import { Browser } from './types'
import errors from '../errors'

// TODO: this is defined in `cypress-npm-api` but there is currently no way to get there
type CypressConfiguration = any
Expand All @@ -35,6 +36,9 @@ type ChromePreferences = {
localState: object
}

const staticCdpPort = Number(process.env.CYPRESS_REMOTE_DEBUGGING_PORT)
const stdioTimeoutMs = Number(process.env.CYPRESS_CDP_TARGET_TIMEOUT) || 60000

const pathToExtension = extension.getPathToExtension()
const pathToTheme = extension.getPathToTheme()

Expand Down Expand Up @@ -171,9 +175,7 @@ const _writeChromePreferences = (userDir: string, originalPrefs: ChromePreferenc
}

const getRemoteDebuggingPort = async () => {
const port = Number(process.env.CYPRESS_REMOTE_DEBUGGING_PORT)

return port || utils.getPort()
return staticCdpPort || utils.getPort()
}

/**
Expand Down Expand Up @@ -241,19 +243,47 @@ const _disableRestorePagesPrompt = function (userDir) {
.catch(() => { })
}

const useStdioCdp = (browser) => {
return (
// CDP via stdio doesn't seem to work in browsers older than 72
// @see https://github.com/cyrus-and/chrome-remote-interface/issues/381#issuecomment-445277683
browser.majorVersion >= 72
// allow users to force TCP by specifying a port in environment
&& !staticCdpPort
)
}

// After the browser has been opened, we can connect to
// its remote interface via a websocket.
const _connectToChromeRemoteInterface = function (port, onError) {
// @ts-ignore
la(check.userPort(port), 'expected port number to connect CRI to', port)
const _connectToChromeRemoteInterface = function (browser, process, port, onError) {
const connectTcp = () => {
// @ts-ignore
la(check.userPort(port), 'expected port number to connect CRI to', port)

debug('connecting to Chrome remote interface at random port %d', port)

return protocol.getWsTargetFor(port)
.then((wsUrl) => {
debug('received wsUrl %s for port %d', wsUrl, port)

return CriClient.create({ target: wsUrl }, onError)
})
}

if (!useStdioCdp(browser)) {
return connectTcp()
}

return CriClient.create({ process }, onError)
.timeout(stdioTimeoutMs)
.catch(Bluebird.TimeoutError, async () => {
errors.warning('CDP_STDIO_TIMEOUT', browser.displayName, stdioTimeoutMs)

debug('connecting to Chrome remote interface at random port %d', port)
const client = await connectTcp()

return protocol.getWsTargetFor(port)
.then((wsUrl) => {
debug('received wsUrl %s for port %d', wsUrl, port)
errors.warning('CDP_FALLBACK_SUCCEEDED', browser.displayName)

return CriClient.create(wsUrl, onError)
return client
})
}

Expand Down Expand Up @@ -405,6 +435,10 @@ export = {
args.push(`--remote-debugging-port=${port}`)
args.push('--remote-debugging-address=127.0.0.1')

if (useStdioCdp(browser)) {
args.push('--remote-debugging-pipe')
}

return args
},

Expand Down Expand Up @@ -460,14 +494,16 @@ export = {
// first allows us to connect the remote interface,
// start video recording and then
// we will load the actual page
const launchedBrowser = await utils.launch(browser, 'about:blank', args)
const launchedBrowser = await utils.launch(browser, 'about:blank', args, {
pipeStdio: useStdioCdp(browser),
})

la(launchedBrowser, 'did not get launched browser instance')

// SECOND connect to the Chrome remote interface
// and when the connection is ready
// navigate to the actual url
const criClient = await this._connectToChromeRemoteInterface(port, options.onError)
const criClient = await this._connectToChromeRemoteInterface(browser, launchedBrowser, port, options.onError)

la(criClient, 'expected Chrome remote interface reference', criClient)

Expand Down
Loading

0 comments on commit acc63c4

Please sign in to comment.