Created
January 8, 2020 20:41
-
-
Save airhorns/d78b5558fb149fa72a46c14d0ad2e837 to your computer and use it in GitHub Desktop.
Browserless-token authentication for lighthouse
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import https from 'https'; | |
import WebSocket from 'ws'; | |
import log from 'lighthouse-logger'; | |
import queryString from 'query-string'; | |
import LighthouseError from 'lighthouse/lighthouse-core/lib/lh-error'; | |
import ChromeProtocol from 'lighthouse/lighthouse-core/gather/connections/cri'; | |
const CONNECT_TIMEOUT = 10000; | |
log.setLevel('info'); | |
// Subclass of lighthouse chrome protocol that supports the ?token= auth setup that browserless uses to auth WS connections. | |
// Browserless uses this token parameter in the URL to authenticate between it's various users, but the lighthouse npm module | |
// expects a no-auth-required chrome instance that it can just connect right to using a host and protocol. This uses the 4th | |
// parameter to the lighthouse function to pass in a custom connection with the little bits overriden to faciliate an | |
// authenticated connection between a local lighthouse client and a remote browserless'd chrome. | |
export class BrowserlessFriendlyChromeProtocol extends ChromeProtocol { | |
constructor(readonly port: string, readonly hostname: string, readonly token: string) { | |
super(port, hostname); | |
} | |
// Override to add the ?token= to the REST API endpoint call | |
_runJsonCommand(command: string) { | |
return new Promise((resolve, reject) => { | |
const request = https.get( | |
{ | |
hostname: this.hostname, | |
port: this.port, | |
path: '/json/' + command + `?token=${this.token}`, | |
}, | |
response => { | |
let data = ''; | |
response.on('data', chunk => { | |
data += chunk; | |
}); | |
response.on('end', () => { | |
if (response.statusCode === 200) { | |
try { | |
resolve(JSON.parse(data)); | |
return; | |
} catch (e) { | |
// In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD | |
if (data === 'Target is closing' || data === 'Target activated') { | |
return resolve({ message: data }); | |
} | |
return reject(e); | |
} | |
} | |
reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`)); | |
}); | |
}, | |
); | |
// This error handler is critical to ensuring Lighthouse exits cleanly even when Chrome crashes. | |
// See https://github.com/GoogleChrome/lighthouse/pull/8583. | |
request.on('error', reject); | |
request.setTimeout(CONNECT_TIMEOUT, () => { | |
// Reject on error with code specifically indicating timeout in connection setup. | |
const err = new LighthouseError(LighthouseError.errors.CRI_TIMEOUT); | |
log.error('CriConnection', err.friendlyMessage); | |
reject(err); | |
request.abort(); | |
}); | |
}); | |
} | |
// Override to add wss:// hack for the returned URL | |
_connectToSocket(response: any) { | |
let url = response.webSocketDebuggerUrl; | |
if (url.startsWith('ws://')) { | |
url = 'wss://' + url.slice(5); | |
} | |
this._pageId = response.id; | |
return new Promise((resolve, reject) => { | |
const ws = new WebSocket(url, { | |
perMessageDeflate: false, | |
}); | |
ws.on('open', () => { | |
this._ws = ws; | |
resolve(); | |
}); | |
ws.on('message', data => this.handleRawMessage(/** @type {string} */ data)); | |
ws.on('close', this.dispose.bind(this)); | |
ws.on('error', reject); | |
}); | |
} | |
} | |
export const lighthouseConnectionFromWSEndpoint = (wsEndpoint: string) => { | |
const url = new URL(wsEndpoint); | |
const params = queryString.parse(url.search); | |
if (params.token) { | |
return new BrowserlessFriendlyChromeProtocol(url.port, url.hostname, params.token as string); | |
} else { | |
return new ChromeProtocol(url.port, url.hostname); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment