123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- 'use strict';
- const TCPProxy = require('tcp-proxy.js');
- const debug = require('debug')('inspector-proxy');
- const urllib = require('urllib');
- const assert = require('assert');
- const EventEmitter = require('events').EventEmitter;
- const KEY = '__ws_proxy__';
- const linkPrefix =
- 'devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:';
- module.exports = class InterceptorProxy extends EventEmitter {
- constructor(options = {}) {
- super();
- const port = options.port;
- assert(port, 'proxy port is needed!');
- this.timeout = null;
- this.silent = !!options.silent;
- this.attached = false;
- this.inspectInfo = null;
- this.proxyPort = port;
- this.proxy = new TCPProxy({ port });
- this.url = `${linkPrefix}${port}/${KEY}`;
- this.proxy.on('close', () => {
- clearTimeout(this.timeout);
- });
- }
- start({ debugPort }) {
- this.debugPort = debugPort;
- return this.end()
- .then(() => {
- this.watchingInspect();
- return new Promise(resolve => this.once('attached', resolve));
- })
- .then(() =>
- this.proxy.createProxy({
- forwardPort: this.debugPort,
- interceptor: {
- client: chunk => {
- if (
- !this.inspectInfo ||
- chunk[0] !== 0x47 || // G
- chunk[1] !== 0x45 || // E
- chunk[2] !== 0x54 || // T
- chunk[3] !== 0x20 // space
- ) {
- return;
- }
- const content = chunk.toString();
- const hasKey = content.includes(KEY);
- debug('request %s', chunk);
- // remind user do not attach again with other client
- if (
- (hasKey || content.includes(this.inspectInfo.id)) &&
- !this.inspectInfo.webSocketDebuggerUrl
- ) {
- debug('inspectInfo %o', this.inspectInfo);
- console.warn(
- "Debugger has been attached, can't attach by other client"
- );
- }
- // replace key to websocket id
- if (hasKey) {
- debug('debugger attach request: %s', chunk);
- return content.replace(KEY, this.inspectInfo.id);
- }
- },
- server: chunk => {
- debug('response %s', chunk);
- },
- },
- })
- );
- }
- end() {
- return this.proxy.end();
- }
- log(info) {
- if (!this.silent) {
- console.log(info);
- }
- }
- watchingInspect(delay = 0) {
- clearTimeout(this.timeout);
- this.timeout = setTimeout(() => {
- urllib
- .request(`http://127.0.0.1:${this.debugPort}/json`, {
- dataType: 'json',
- })
- .then(({ data }) => {
- this.attach(data && data[0]);
- })
- .catch(e => {
- this.detach(e);
- });
- }, delay);
- }
- attach(data) {
- if (!this.attached) {
- this.log(`${this.debugPort} opened`);
- debug(`attached ${this.debugPort}: %O`, data);
- }
- this.attached = true;
- this.emit('attached', (this.inspectInfo = data));
- this.watchingInspect(1000);
- }
- detach(e) {
- if (e.code === 'HPE_INVALID_CONSTANT') {
- // old debugger protocol, it's not http response
- debug('legacy protocol');
- return this.attach();
- }
- if (this.attached) {
- this.emit('detached');
- this.log(`${this.debugPort} closed`);
- debug(`detached ${this.debugPort}: %O`, e);
- }
- this.attached = false;
- this.watchingInspect(1000);
- }
- };
|