allproxy
Version:
AllProxy: MITM HTTP Debugging Tool.
405 lines • 18.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FreshOpera = exports.FreshBrave = exports.FreshEdgeCanary = exports.FreshEdgeDev = exports.FreshEdgeBeta = exports.FreshEdge = exports.FreshChromiumDev = exports.FreshChromium = exports.FreshChromeCanary = exports.FreshChromeDev = exports.FreshChromeBeta = exports.ExistingChrome = exports.FreshChrome = void 0;
const _ = __importStar(require("lodash"));
const mockttp_1 = require("mockttp");
const browsers_1 = require("./browsers");
const promise_1 = require("./util/promise");
const fs_1 = require("./util/fs");
const process_management_1 = require("./util/process-management");
const hide_warning_server_1 = require("./hide-warning-server");
const getBrowserDetails = (config, variant) => __awaiter(void 0, void 0, void 0, function* () {
const browsers = yield (0, browsers_1.getAvailableBrowsers)(config.configPath);
// Get the details for the first of these browsers that is installed.
return _.find(browsers, b => b.name === variant);
});
const getChromiumLaunchOptions = (browser, config, proxyPort, hideWarningServer) => __awaiter(void 0, void 0, void 0, function* () {
const certificatePem = yield (0, fs_1.readFile)(config.https.certPath, 'utf8');
const spkiFingerprint = (0, mockttp_1.generateSPKIFingerprint)(certificatePem);
return {
browser,
proxy: `http://127.0.0.1:${proxyPort}`,
noProxy: [
// Force even localhost requests to go through the proxy
// See https://bugs.chromium.org/p/chromium/issues/detail?id=899126#c17
'<-loopback>',
// Don't intercept our warning hiding requests. Note that this must be
// the 2nd rule here, or <-loopback> would override it.
hideWarningServer.host
],
options: [
// Trust our CA certificate's fingerprint:
`--ignore-certificate-errors-spki-list=${spkiFingerprint}`,
`--ignore-certificate-errors`,
// Disable annoying "What's New" page
'--disable-features=ChromeWhatsNewUI',
// Avoid annoying extra network noise:
'--disable-background-networking',
'--disable-component-update',
'--check-for-update-interval=31536000' // Don't update for a year
]
};
});
class FreshChromiumBasedInterceptor {
constructor(config, variantName) {
this.config = config;
this.variantName = variantName;
this.activeBrowsers = {};
}
isActive(proxyPort) {
const browser = this.activeBrowsers[proxyPort];
return !!browser && !!browser.pid;
}
isActivable() {
return __awaiter(this, void 0, void 0, function* () {
const browserDetails = yield getBrowserDetails(this.config, this.variantName);
return !!browserDetails;
});
}
activate(proxyPort) {
return __awaiter(this, void 0, void 0, function* () {
if (this.isActive(proxyPort))
return;
const hideWarningServer = new hide_warning_server_1.HideWarningServer(this.config);
yield hideWarningServer.start('https://google.com');
const browserDetails = yield getBrowserDetails(this.config, this.variantName);
const browser = yield (0, browsers_1.launchBrowser)(hideWarningServer.hideWarningUrl, yield getChromiumLaunchOptions(browserDetails ? browserDetails.name : this.variantName, this.config, proxyPort, hideWarningServer), this.config.configPath);
if (browser.process.stdout)
browser.process.stdout.pipe(process.stdout);
if (browser.process.stderr)
browser.process.stderr.pipe(process.stderr);
yield hideWarningServer.completedPromise;
yield hideWarningServer.stop();
this.activeBrowsers[proxyPort] = browser;
browser.process.once('close', () => __awaiter(this, void 0, void 0, function* () {
delete this.activeBrowsers[proxyPort];
// Opera has a launch proc that exits immediately in Windows, so we can't clear the profile there.
if (process.platform === 'win32' && this.variantName === 'opera')
return;
yield (0, promise_1.delay)(1000); // No hurry, make sure the browser & related processes have all cleaned up
if (Object.keys(this.activeBrowsers).length === 0 && browserDetails && _.isString(browserDetails.profile)) {
// If we were the last browser, and we have a profile path, and it's in our config
// (just in case something's gone wrong) -> delete the profile to reset everything.
const profilePath = browserDetails.profile;
if (!profilePath.startsWith(this.config.configPath)) {
console.error(`Unexpected ${this.variantName} profile location, not deleting: ${profilePath}`);
}
else {
const profileFolder = browserDetails.profile;
(0, fs_1.deleteFolder)(profileFolder)
.catch(() => __awaiter(this, void 0, void 0, function* () {
// After 1 error, wait a little and retry
yield (0, promise_1.delay)(1000);
yield (0, fs_1.deleteFolder)(profileFolder);
})).catch(console.warn); // If it still fails, just give up, not a big deal
}
}
}));
// Delay the approx amount of time it normally takes the browser to really open, just to be sure
yield (0, promise_1.delay)(500);
});
}
deactivate(proxyPort) {
return __awaiter(this, void 0, void 0, function* () {
if (this.isActive(proxyPort)) {
const browser = this.activeBrowsers[proxyPort];
const exitPromise = new Promise((resolve) => browser.process.once('close', resolve));
browser.stop();
yield exitPromise;
}
});
}
deactivateAll() {
return __awaiter(this, void 0, void 0, function* () {
yield Promise.all(Object.keys(this.activeBrowsers).map((proxyPort) => this.deactivate(proxyPort)));
});
}
}
;
class ExistingChromiumBasedInterceptor {
constructor(config, variantName) {
this.config = config;
this.variantName = variantName;
}
browserDetails() {
return __awaiter(this, void 0, void 0, function* () {
return getBrowserDetails(this.config, this.variantName);
});
}
isActive(proxyPort) {
const activeBrowser = this.activeBrowser;
return !!activeBrowser &&
activeBrowser.proxyPort === proxyPort &&
!!activeBrowser.browser.pid;
}
isActivable() {
return __awaiter(this, void 0, void 0, function* () {
if (this.activeBrowser)
return false;
return !!(yield this.browserDetails());
});
}
findExistingPid() {
return __awaiter(this, void 0, void 0, function* () {
const processes = yield (0, process_management_1.listRunningProcesses)();
const browserDetails = yield this.browserDetails();
if (!browserDetails) {
throw new Error("Can't intercept existing browser without browser details");
}
const browserProcesses = processes.filter((proc) => {
if (process.platform === 'darwin') {
if (!proc.command.startsWith(browserDetails.command))
return false;
const appBundlePath = proc.command.substring(browserDetails.command.length);
// Only *.app/Contents/MacOS/* is the main app process:
return appBundlePath.match(/^\/Contents\/MacOS\//);
}
else {
return proc.bin && (
// Find a binary that exactly matches the specific command:
proc.bin === browserDetails.command ||
// Or whose binary who's matches the path for this specific variant:
proc.bin.includes(`${browserDetails.name}/${browserDetails.type}`));
}
});
const defaultRootProcess = browserProcesses.find(({ args }) => args !== undefined &&
// Find the main process, skipping any renderer/util processes
!args.includes('--type=') &&
// Also skip any non-default profile processes (e.g. our own fresh Chromes)
!args.includes('--user-data-dir'));
return defaultRootProcess && defaultRootProcess.pid;
});
}
activate(proxyPort, options = { closeConfirmed: false }) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.isActivable())
return;
const hideWarningServer = new hide_warning_server_1.HideWarningServer(this.config);
yield hideWarningServer.start('https://google.com');
const existingPid = yield this.findExistingPid();
if (existingPid) {
if (!options.closeConfirmed) {
// Fail, with metadata requesting the UI to confirm that Chrome should be killed
throw Object.assign(new Error(`Not killing ${this.variantName}: not confirmed`), { metadata: { closeConfirmRequired: true }, reportable: false });
}
if (process.platform === 'win32') {
(0, process_management_1.windowsClose)(existingPid);
try {
yield (0, process_management_1.waitForExit)(existingPid);
}
catch (e) {
// Try again, but less gently this time:
process.kill(existingPid);
yield (0, process_management_1.waitForExit)(existingPid);
}
}
else {
process.kill(existingPid);
yield (0, process_management_1.waitForExit)(existingPid);
}
}
const browserDetails = yield getBrowserDetails(this.config, this.variantName);
const launchOptions = yield getChromiumLaunchOptions(browserDetails ? browserDetails.name : this.variantName, this.config, proxyPort, hideWarningServer);
// Remove almost all default arguments. Each of these changes behaviour in maybe unexpected
// ways, notably including --disable-restore which actively causes problems.
launchOptions.skipDefaults = true;
launchOptions.options.push('--no-default-browser-check', '--no-first-run', '--disable-popup-blocking', // Required for hide-warning -> amiusing hop
// If we killed something, use --restore-last-session to ensure it comes back:
...(existingPid ? ['--restore-last-session'] : []),
// Passing the URL here instead of passing it to launchBrowser ensures that it isn't
// opened in a separate window when launching on Mac
hideWarningServer.hideWarningUrl);
const browser = yield (0, browsers_1.launchBrowser)("", Object.assign(Object.assign({}, launchOptions), { skipDefaults: true, profile: null // Enforce that we use the default profile
}), this.config.configPath);
if (browser.process.stdout)
browser.process.stdout.pipe(process.stdout);
if (browser.process.stderr)
browser.process.stderr.pipe(process.stderr);
yield hideWarningServer.completedPromise;
yield hideWarningServer.stop();
this.activeBrowser = { browser, proxyPort };
browser.process.once('close', () => __awaiter(this, void 0, void 0, function* () {
delete this.activeBrowser;
}));
// Delay the approx amount of time it normally takes the browser to really open, just to be sure
yield (0, promise_1.delay)(500);
});
}
deactivate(proxyPort) {
return __awaiter(this, void 0, void 0, function* () {
if (this.isActive(proxyPort)) {
const { browser } = this.activeBrowser;
if (process.platform === 'win32') {
// Try to cleanly close if we can, rather than killing Chrome directly:
try {
yield (0, process_management_1.windowsClose)(browser.pid)
.then(() => (0, process_management_1.waitForExit)(browser.pid));
return;
}
catch (e) { } // If this fails/times out, kill like we do elsewhere:
}
const exitPromise = new Promise((resolve) => browser.process.once('close', resolve));
browser.stop();
yield exitPromise;
}
});
}
deactivateAll() {
return __awaiter(this, void 0, void 0, function* () {
if (this.activeBrowser) {
yield this.deactivate(this.activeBrowser.proxyPort);
}
});
}
}
;
class FreshChrome extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'chrome');
this.id = 'fresh-chrome';
this.version = '1.0.0';
}
}
exports.FreshChrome = FreshChrome;
;
class ExistingChrome extends ExistingChromiumBasedInterceptor {
constructor(config) {
super(config, 'chrome');
this.id = 'existing-chrome';
this.version = '1.0.0';
}
}
exports.ExistingChrome = ExistingChrome;
;
class FreshChromeBeta extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'chrome-beta');
this.id = 'fresh-chrome-beta';
this.version = '1.0.0';
}
}
exports.FreshChromeBeta = FreshChromeBeta;
;
class FreshChromeDev extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'chrome-dev');
this.id = 'fresh-chrome-dev';
this.version = '1.0.0';
}
}
exports.FreshChromeDev = FreshChromeDev;
;
class FreshChromeCanary extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'chrome-canary');
this.id = 'fresh-chrome-canary';
this.version = '1.0.0';
}
}
exports.FreshChromeCanary = FreshChromeCanary;
;
class FreshChromium extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'chromium');
this.id = 'fresh-chromium';
this.version = '1.0.0';
}
}
exports.FreshChromium = FreshChromium;
;
class FreshChromiumDev extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'chromium-dev');
this.id = 'fresh-chromium-dev';
this.version = '1.0.0';
}
}
exports.FreshChromiumDev = FreshChromiumDev;
;
class FreshEdge extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'msedge');
this.id = 'fresh-edge';
this.version = '1.0.0';
}
}
exports.FreshEdge = FreshEdge;
;
class FreshEdgeBeta extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'msedge-beta');
this.id = 'fresh-edge-beta';
this.version = '1.0.0';
}
}
exports.FreshEdgeBeta = FreshEdgeBeta;
;
class FreshEdgeDev extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'msedge-dev');
this.id = 'fresh-edge-dev';
this.version = '1.0.0';
}
}
exports.FreshEdgeDev = FreshEdgeDev;
;
class FreshEdgeCanary extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'msedge-canary');
this.id = 'fresh-edge-canary';
this.version = '1.0.0';
}
}
exports.FreshEdgeCanary = FreshEdgeCanary;
;
class FreshBrave extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'brave');
this.id = 'fresh-brave';
this.version = '1.0.0';
}
}
exports.FreshBrave = FreshBrave;
;
class FreshOpera extends FreshChromiumBasedInterceptor {
constructor(config) {
super(config, 'opera');
this.id = 'fresh-opera';
this.version = '1.0.3';
}
}
exports.FreshOpera = FreshOpera;
;
//# sourceMappingURL=chromium-based-interceptors.js.map