allproxy
Version:
AllProxy: MITM HTTP Debugging Tool.
282 lines • 14.4 kB
JavaScript
;
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.FreshTerminalInterceptor = void 0;
const _ = __importStar(require("lodash"));
const child_process_1 = require("child_process");
const GSettings = __importStar(require("node-gsettings-wrapper"));
const osx_find_executable_1 = require("@httptoolkit/osx-find-executable");
const error_1 = require("./util/error");
const fs_1 = require("./util/fs");
const process_management_1 = require("./util/process-management");
const terminal_env_overrides_1 = require("./terminal-env-overrides");
const terminal_scripts_1 = require("./terminal-scripts");
const DEFAULT_GIT_BASH_PATH = 'C:/Program Files/git/git-bash.exe';
const getTerminalCommand = _.memoize(() => __awaiter(void 0, void 0, void 0, function* () {
let result;
if (process.platform === 'win32')
result = getWindowsTerminalCommand();
else if (process.platform === 'darwin')
result = getOSXTerminalCommand();
else if (process.platform === 'linux')
result = getLinuxTerminalCommand();
else
result = Promise.resolve(null);
result.then((terminal) => {
if (!terminal)
console.log('No terminal could be detected');
});
return result;
}));
const getWindowsTerminalCommand = () => __awaiter(void 0, void 0, void 0, function* () {
if (yield (0, fs_1.canAccess)(DEFAULT_GIT_BASH_PATH)) {
return { command: DEFAULT_GIT_BASH_PATH };
}
else if (yield (0, fs_1.commandExists)('git-bash')) {
return { command: 'git-bash' };
}
return { command: 'start', args: ['cmd'], options: { shell: true }, skipStartupScripts: true };
});
const getOSXTerminalCommand = () => __awaiter(void 0, void 0, void 0, function* () {
const terminalExecutables = (yield Promise.all([
'co.zeit.hyper',
'com.googlecode.iterm2',
'com.googlecode.iterm',
'com.apple.Terminal'
].map((bundleId) => (0, osx_find_executable_1.findExecutableById)(bundleId).catch(() => null)))).filter((executablePath) => !!executablePath);
const bestAvailableTerminal = terminalExecutables[0];
if (bestAvailableTerminal)
return { command: bestAvailableTerminal };
else
return null;
});
const getLinuxTerminalCommand = () => __awaiter(void 0, void 0, void 0, function* () {
// Symlink/wrapper that should indicate the system default
if (yield (0, fs_1.commandExists)('x-terminal-emulator'))
return getXTerminalCommand();
// Check gnome app settings, if available
if (GSettings.isAvailable()) {
const gSettingsTerminalKey = GSettings.Key.findById('org.gnome.desktop.default-applications.terminal', 'exec');
const defaultTerminal = gSettingsTerminalKey && gSettingsTerminalKey.getValue();
if (defaultTerminal && (yield (0, fs_1.commandExists)(defaultTerminal))) {
if (defaultTerminal.includes('gnome-terminal'))
return getGnomeTerminalCommand(defaultTerminal);
if (defaultTerminal.includes('konsole'))
return getKonsoleTerminalCommand(defaultTerminal);
if (defaultTerminal.includes('xfce4-terminal'))
return getXfceTerminalCommand(defaultTerminal);
if (defaultTerminal.includes('x-terminal-emulator'))
return getXTerminalCommand(defaultTerminal);
if (defaultTerminal.includes('terminator'))
return { command: 'terminator', args: ['-u'] };
return { command: defaultTerminal };
}
}
// If a specific term like this is installed, it's probably the preferred one
if (yield (0, fs_1.commandExists)('konsole'))
return getKonsoleTerminalCommand();
if (yield (0, fs_1.commandExists)('xfce4-terminal'))
return getXfceTerminalCommand();
if (yield (0, fs_1.commandExists)('kitty'))
return { command: 'kitty' };
if (yield (0, fs_1.commandExists)('urxvt'))
return { command: 'urxvt' };
if (yield (0, fs_1.commandExists)('rxvt'))
return { command: 'rxvt' };
if (yield (0, fs_1.commandExists)('termit'))
return { command: 'termit' };
if (yield (0, fs_1.commandExists)('terminator'))
return { command: 'terminator', args: ['-u'] };
if (yield (0, fs_1.commandExists)('alacritty'))
return { command: 'alacritty' };
if (yield (0, fs_1.commandExists)('uxterm'))
return { command: 'uxterm' };
if (yield (0, fs_1.commandExists)('xterm'))
return { command: 'xterm' };
return null;
});
const getXTerminalCommand = (command = 'x-terminal-emulator') => __awaiter(void 0, void 0, void 0, function* () {
var _a;
// x-terminal-emulator is a wrapper/symlink to the terminal of choice.
// Unfortunately, we need to pass specific args that aren't supported by all terminals (to ensure
// terminals run in the foreground), and the Debian XTE wrapper at least doesn't pass through
// any of the args we want to use. To fix this, we parse --help to try and detect the underlying
// terminal, and run it directly with the args we need.
try {
// Run the command with -h to get some output we can use to infer the terminal itself.
// --version would be nice, but the debian wrapper ignores it. --help isn't supported by xterm.
const { stdout } = yield (0, process_management_1.spawnToResult)(command, ['-h']);
const helpOutput = stdout.toLowerCase().replace(/[^\w\d]+/g, ' ');
if (helpOutput.includes('gnome terminal') && (yield (0, fs_1.commandExists)('gnome-terminal'))) {
return getGnomeTerminalCommand();
}
else if (helpOutput.includes('xfce4 terminal') && (yield (0, fs_1.commandExists)('xfce4-terminal'))) {
return getXfceTerminalCommand();
}
else if (helpOutput.includes('konsole') && (yield (0, fs_1.commandExists)('konsole'))) {
return getKonsoleTerminalCommand();
}
}
catch (e) {
if ((0, error_1.isErrorLike)(e) && ((_a = e.message) === null || _a === void 0 ? void 0 : _a.includes('rxvt'))) {
// Bizarrely, rxvt -h prints help but always returns a non-zero exit code.
// Doesn't need any special arguments anyway though, so just ignore it
}
else {
console.log(e);
}
}
// If there's an error, or we just don't recognize the console, give up & run it directly
return { command };
});
const getKonsoleTerminalCommand = (command = 'konsole') => __awaiter(void 0, void 0, void 0, function* () {
let extraArgs = [];
const { stdout } = yield (0, process_management_1.spawnToResult)(command, ['--help']);
// Forces Konsole to run in the foreground, with no separate process
// Seems to be well supported for a long time, but check just in case
if (stdout.includes('--nofork')) {
extraArgs = ['--nofork'];
}
return { command, args: extraArgs };
});
const getGnomeTerminalCommand = (command = 'gnome-terminal') => __awaiter(void 0, void 0, void 0, function* () {
let extraArgs = [];
const { stdout } = yield (0, process_management_1.spawnToResult)(command, ['--help-all']);
// Officially supported option, but only supported in v3.28+
if (stdout.includes('--wait')) {
extraArgs = ['--wait'];
}
else {
// Debugging option - works back to v3.7 (2012), but not officially supported
// Documented at https://wiki.gnome.org/Apps/Terminal/Debugging
const randomId = Math.round((Math.random() * 100000));
extraArgs = ['--app-id', `com.httptoolkit.${randomId}`];
}
// We're assuming here that nobody is developing in a pre-2012 un-updated gnome-terminal.
// If they are then gnome-terminal is not going to recognize --app-id, and will fail to
// start. Hard to avoid, rare case, so c'est la vie.
return { command, args: extraArgs };
});
const getXfceTerminalCommand = (command = 'xfce4-terminal') => __awaiter(void 0, void 0, void 0, function* () {
let extraArgs = [];
const { stdout } = yield (0, process_management_1.spawnToResult)(command, ['--help']);
// Disables the XFCE terminal server for this terminal, so it runs in the foreground.
// Seems to be well supported for a long time, but check just in case
if (stdout.includes('--disable-server')) {
extraArgs = ['--disable-server'];
}
return { command, args: extraArgs };
});
const terminals = {};
class FreshTerminalInterceptor {
constructor(config) {
this.config = config;
this.id = 'fresh-terminal';
this.version = '1.0.0';
}
isActivable() {
return __awaiter(this, void 0, void 0, function* () {
return !!(yield getTerminalCommand());
});
}
isActive(proxyPort) {
return !!(terminals[proxyPort] && terminals[proxyPort].length);
}
activate(proxyPort) {
return __awaiter(this, void 0, void 0, function* () {
const terminalSpawnArgs = yield getTerminalCommand();
if (!terminalSpawnArgs)
throw new Error('Could not find a suitable terminal');
const { command, args, options, skipStartupScripts } = terminalSpawnArgs;
// Our PATH override below may not work, e.g. because OSX's path_helper always prepends
// the real paths over the top, and git-bash ignore env var paths overrides. To fix this,
// we (very carefully!) rewrite shell startup scripts, to reset the PATH in our shell.
// This gets reset on exit, and is behind a flag so it won't affect other shells anyway.
if (!skipStartupScripts)
yield (0, terminal_scripts_1.editShellStartupScripts)();
const currentEnv = (process.platform === 'win32')
// Windows env var behaviour is very odd. Windows env vars are case-insensitive, and node
// simulates this for process.env accesses, but when used in an object they become
// case-*sensitive* object keys, and it's easy to end up with duplicates.
// To fix this, on Windows we enforce here that all env var input keys are uppercase.
? _.mapKeys(process.env, (_value, key) => key.toUpperCase())
: process.env;
const childProc = (0, child_process_1.spawn)(command, (args || []), _.assign(options || {}, {
env: Object.assign(Object.assign({}, currentEnv), (0, terminal_env_overrides_1.getTerminalEnvVars)(proxyPort, this.config.https, currentEnv, {})),
cwd: currentEnv.HOME || currentEnv.USERPROFILE
}));
terminals[proxyPort] = (terminals[proxyPort] || []).concat(childProc);
const onTerminalClosed = () => {
terminals[proxyPort] = _.reject(terminals[proxyPort], childProc);
// Delay slightly as some terminals (gnome-terminal) exit immediately,
// and start the terminal elsewhere, so it may not have started yet.
setTimeout(() => {
if (_.every(terminals, ts => _.isEmpty(ts)))
(0, terminal_scripts_1.resetShellStartupScripts)();
}, 500);
};
childProc.once('close', onTerminalClosed);
childProc.once('error', (e) => {
console.log(e);
onTerminalClosed();
});
// Watch for spawn errors immediately after startup to judge whether the
// terminal launch was actually successful:
return new Promise((resolve, reject) => {
setTimeout(resolve, 500); // If it doesn't crash within 500ms, it's probably good
childProc.once('error', reject); // If it does crash, it's definitely not.
});
});
}
deactivate(proxyPort) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.isActive(proxyPort))
return;
yield Promise.all((terminals[proxyPort] || []).map((proc) => {
return new Promise((resolve) => {
proc.once('close', resolve);
proc.kill();
});
}));
});
}
deactivateAll() {
return __awaiter(this, void 0, void 0, function* () {
yield Promise.all(Object.keys(terminals).map((proxyPort) => this.deactivate(proxyPort)));
yield (0, terminal_scripts_1.resetShellStartupScripts)();
});
}
}
exports.FreshTerminalInterceptor = FreshTerminalInterceptor;
//# sourceMappingURL=fresh-terminal-interceptor.js.map