Skip to content

Commit 748081f

Browse files
committed
Fix launching Snap-based Chromiums
1 parent c15dfd2 commit 748081f

File tree

3 files changed

+34
-18
lines changed

3 files changed

+34
-18
lines changed

src/interceptors/chromium-based-interceptors.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import _ from 'lodash';
2+
import * as path from 'path';
23
import { generateSPKIFingerprint } from 'mockttp';
34

45
import { HtkConfig } from '../config';
56

7+
import { delay } from '../util/promise';
8+
import { readFile, deleteFolder } from '../util/fs';
9+
import { listRunningProcesses, windowsClose, waitForExit } from '../util/process-management';
10+
import { getSnapConfigPath, isSnap } from '../util/snap';
11+
612
import {
713
getBrowserDetails,
814
launchBrowser,
915
BrowserInstance,
1016
LaunchOptions
1117
} from '../browsers';
12-
import { delay } from '../util/promise';
13-
import { readFile, deleteFolder } from '../util/fs';
14-
import { listRunningProcesses, windowsClose, waitForExit } from '../util/process-management';
1518
import { HideWarningServer } from '../hide-warning-server';
1619
import { Interceptor } from '.';
1720
import { logError } from '../error-tracking';
@@ -20,6 +23,7 @@ import { WEBEXTENSION_INSTALL } from '../webextension';
2023
const getChromiumLaunchOptions = async (
2124
browser: string,
2225
config: HtkConfig,
26+
profilePath: string | null | undefined,
2327
proxyPort: number,
2428
hideWarningServer: HideWarningServer,
2529
webExtensionEnabled: boolean
@@ -28,6 +32,7 @@ const getChromiumLaunchOptions = async (
2832
const spkiFingerprint = generateSPKIFingerprint(certificatePem);
2933

3034
return {
35+
profile: profilePath,
3136
browser,
3237
proxy: `https://127.0.0.1:${proxyPort}`,
3338
noProxy: [
@@ -95,10 +100,15 @@ abstract class FreshChromiumBasedInterceptor implements Interceptor {
95100

96101
const browserDetails = await getBrowserDetails(this.config.configPath, this.variantName);
97102

103+
const profilePath = browserDetails && await isSnap(browserDetails.command)
104+
? path.join(await getSnapConfigPath(this.variantName), 'profile')
105+
: undefined;
106+
98107
const browser = await launchBrowser(hideWarningServer.hideWarningUrl,
99108
await getChromiumLaunchOptions(
100109
browserDetails ? browserDetails.name : this.variantName,
101110
this.config,
111+
profilePath,
102112
proxyPort,
103113
hideWarningServer,
104114
!!options.webExtensionEnabled
@@ -121,7 +131,7 @@ abstract class FreshChromiumBasedInterceptor implements Interceptor {
121131
if (process.platform === 'win32' && this.variantName === 'opera') return;
122132
await delay(1000); // No hurry, make sure the browser & related processes have all cleaned up
123133

124-
if (Object.keys(this.activeBrowsers).length === 0 && browserDetails && _.isString(browserDetails.profile)) {
134+
if (Object.keys(this.activeBrowsers).length === 0 && typeof browserDetails?.profile === 'string') {
125135
// If we were the last browser, and we have a profile path, and it's in our config
126136
// (just in case something's gone wrong) -> delete the profile to reset everything.
127137

@@ -269,6 +279,7 @@ abstract class ExistingChromiumBasedInterceptor implements Interceptor {
269279
const launchOptions = await getChromiumLaunchOptions(
270280
browserDetails ? browserDetails.name : this.variantName,
271281
this.config,
282+
null, // Null profile path ensures we use the system default profile
272283
proxyPort,
273284
hideWarningServer,
274285
!!options.webExtensionEnabled
@@ -290,8 +301,7 @@ abstract class ExistingChromiumBasedInterceptor implements Interceptor {
290301

291302
const browser = await launchBrowser("", {
292303
...launchOptions,
293-
skipDefaults: true,
294-
profile: null // Enforce that we use the default profile
304+
skipDefaults: true
295305
}, this.config.configPath);
296306

297307
if (browser.process.stdout) browser.process.stdout.pipe(process.stdout);

src/interceptors/fresh-firefox.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export class FreshFirefox implements Interceptor {
243243
if (!browserDetails) throw new Error('Firefox could not be detected');
244244

245245
const profilePath = await isSnap(browserDetails.command)
246-
? path.join(getSnapConfigPath('firefox'), 'profile')
246+
? path.join(await getSnapConfigPath('firefox'), 'profile')
247247
: path.join(this.config.configPath, 'firefox-profile');
248248

249249
const firefoxPrefsFile = path.join(profilePath, 'prefs.js');

src/util/snap.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import os = require('os');
22
import path = require('path');
33

44
import fs = require('./fs');
5+
import { streamToBuffer } from './stream';
56

67
export async function isSnap(bin: string) {
78
if (os.platform() !== 'linux') return false;
@@ -14,24 +15,29 @@ export async function isSnap(bin: string) {
1415
// Most snaps directly run from the Snap bin folder:
1516
if (binPath.startsWith('/snap/bin/')) return true;
1617

17-
// Firefox is the only known example that doesn't - it uses a
18-
// wrapper script, so we just look for that:
19-
if (binPath === '/usr/bin/firefox') {
20-
const content = await fs.readFile(binPath);
21-
return content.includes('exec /snap/bin/firefox');
22-
}
18+
// If not, the command might be a wrapper script - both chromium-browser
19+
// & firefox use these. Check the end and see if we recognize it:
20+
21+
const fileSize = await fs.statFile(binPath);
22+
const stream = fs.createReadStream(binPath, { start: fileSize.size - 100 });
23+
const lastChunkOfFile = (await streamToBuffer(stream)).toString('utf8');
2324

24-
return false;
25+
return lastChunkOfFile.includes('exec /snap/bin/');
2526
}
2627

2728
// For all Snaps, any data we want to inject needs to live inside the
2829
// Snap's data directory - we put it in a .httptoolkit folder.
29-
export const getSnapConfigPath = (appName: string) => {
30-
return path.join(
30+
export async function getSnapConfigPath(appName: string) {
31+
const snapDataPath = path.join(
3132
os.homedir(),
3233
'snap',
3334
appName,
34-
'current',
35-
'.httptoolkit'
35+
'current'
3636
);
37+
38+
if (!await fs.canAccess(snapDataPath)) {
39+
throw new Error(`Could not find Snap data path for ${appName}`);
40+
}
41+
42+
return path.join(snapDataPath, '.httptoolkit');
3743
}

0 commit comments

Comments
 (0)