fix(electron): do not use async callback in onBeforeSendHeaders (#7894)

it seems using async callback for onBeforeSendHeaders may crash the app.
the main reason why it is async is to read the cookies from session.

fix AF-1172
This commit is contained in:
pengx17
2024-08-16 05:17:47 +00:00
parent b57ce4646f
commit 69c507fded
3 changed files with 45 additions and 77 deletions
@@ -1,7 +1,6 @@
import path from 'node:path'; import path from 'node:path';
import type { App, BrowserWindow } from 'electron'; import type { App } from 'electron';
import { ipcMain } from 'electron';
import { buildType, CLOUD_BASE_URL, isDev } from './config'; import { buildType, CLOUD_BASE_URL, isDev } from './config';
import { mainWindowOrigin } from './constants'; import { mainWindowOrigin } from './constants';
@@ -103,20 +102,8 @@ async function handleOauthJwt(url: string) {
), ),
}); });
let hiddenWindow: BrowserWindow | null = null;
ipcMain.once('affine:login', () => {
hiddenWindow?.destroy();
if (urlObj.searchParams.get('next') === 'onboarding') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
mainWindow.loadURL(mainWindowOrigin + '/auth/onboarding');
}
});
// hacks to refresh auth state in the main window // hacks to refresh auth state in the main window
hiddenWindow = await handleOpenUrlInHiddenWindow( await handleOpenUrlInHiddenWindow(mainWindowOrigin + '/auth/signIn');
mainWindowOrigin + '/auth/signIn'
);
} catch (e) { } catch (e) {
logger.error('failed to open url in popup', e); logger.error('failed to open url in popup', e);
} }
+17 -28
View File
@@ -3,8 +3,7 @@ import { join } from 'node:path';
import { net, protocol, session } from 'electron'; import { net, protocol, session } from 'electron';
import { CLOUD_BASE_URL } from './config'; import { CLOUD_BASE_URL } from './config';
import { logger } from './logger'; import { getCookies } from './windows-manager';
import { getCookie } from './windows-manager';
protocol.registerSchemesAsPrivileged([ protocol.registerSchemesAsPrivileged([
{ {
@@ -104,33 +103,23 @@ export function registerProtocol() {
); );
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => { session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
(async () => { const url = new URL(details.url);
const url = new URL(details.url); const pathname = url.pathname;
const pathname = url.pathname; // if sending request to the cloud, attach the session cookie
// if sending request to the cloud, attach the session cookie if (isNetworkResource(pathname)) {
if (isNetworkResource(pathname)) { const cookie = getCookies();
const cookie = await getCookie(CLOUD_BASE_URL); if (cookie) {
if (cookie) { const cookieString = cookie.map(c => `${c.name}=${c.value}`).join('; ');
const cookieString = cookie details.requestHeaders['cookie'] = cookieString;
.map(c => `${c.name}=${c.value}`)
.join('; ');
details.requestHeaders['cookie'] = cookieString;
}
// add the referer and origin headers
details.requestHeaders['referer'] ??= CLOUD_BASE_URL;
details.requestHeaders['origin'] ??= CLOUD_BASE_URL;
} }
callback({
cancel: false, // add the referer and origin headers
requestHeaders: details.requestHeaders, details.requestHeaders['referer'] ??= CLOUD_BASE_URL;
}); details.requestHeaders['origin'] ??= CLOUD_BASE_URL;
})().catch(e => { }
logger.error('failed to attach cookie', e); callback({
callback({ cancel: false,
cancel: false, requestHeaders: details.requestHeaders,
requestHeaders: details.requestHeaders,
});
}); });
}); });
} }
@@ -3,6 +3,7 @@ import { join } from 'node:path';
import { import {
app, app,
type CookiesSetDetails, type CookiesSetDetails,
session,
type View, type View,
type WebContents, type WebContents,
WebContentsView, WebContentsView,
@@ -22,7 +23,7 @@ import {
} from 'rxjs'; } from 'rxjs';
import { isMacOS } from '../../shared/utils'; import { isMacOS } from '../../shared/utils';
import { isDev } from '../config'; import { CLOUD_BASE_URL, isDev } from '../config';
import { mainWindowOrigin, shellViewUrl } from '../constants'; import { mainWindowOrigin, shellViewUrl } from '../constants';
import { ensureHelperProcess } from '../helper-process'; import { ensureHelperProcess } from '../helper-process';
import { logger } from '../logger'; import { logger } from '../logger';
@@ -185,6 +186,8 @@ export class WebContentViewsManager {
*/ */
readonly tabAction$ = new Subject<TabAction>(); readonly tabAction$ = new Subject<TabAction>();
cookies: Electron.Cookie[] = [];
readonly activeWorkbenchId$ = this.tabViewsMeta$.pipe( readonly activeWorkbenchId$ = this.tabViewsMeta$.pipe(
map(m => m?.activeWorkbenchId ?? m?.workbenches[0].id) map(m => m?.activeWorkbenchId ?? m?.workbenches[0].id)
); );
@@ -672,7 +675,6 @@ export class WebContentViewsManager {
disposables.push( disposables.push(
windowReadyToShow$.subscribe(w => { windowReadyToShow$.subscribe(w => {
handleWebContentsResize().catch(logger.error); handleWebContentsResize().catch(logger.error);
const screenSizeChangeEvents = ['resize', 'maximize', 'unmaximize']; const screenSizeChangeEvents = ['resize', 'maximize', 'unmaximize'];
const onResize = () => { const onResize = () => {
if (this.activeWorkbenchView) { if (this.activeWorkbenchView) {
@@ -689,6 +691,23 @@ export class WebContentViewsManager {
// add shell view // add shell view
this.createAndAddView('shell').catch(logger.error); this.createAndAddView('shell').catch(logger.error);
(async () => { (async () => {
const updateCookies = () => {
session.defaultSession.cookies
.get({
url: CLOUD_BASE_URL,
})
.then(cookies => {
this.cookies = cookies;
})
.catch(err => {
logger.error('failed to get cookies', err);
});
};
updateCookies();
session.defaultSession.cookies.on('changed', () => {
updateCookies();
});
if (this.tabViewsMeta.workbenches.length === 0) { if (this.tabViewsMeta.workbenches.length === 0) {
// create a default view (e.g., on first launch) // create a default view (e.g., on first launch)
await this.addTab(); await this.addTab();
@@ -705,40 +724,17 @@ export class WebContentViewsManager {
}); });
}; };
setCookie = async (cookie: CookiesSetDetails) => { setCookie = async (cookiesSetDetails: CookiesSetDetails) => {
const views = this.allViews; const views = this.allViews;
if (!views) { if (!views) {
return; return;
} }
logger.info('setting cookie to main window view(s)', cookie); logger.info('setting cookie to main window view(s)', cookiesSetDetails);
for (const view of views) { for (const view of views) {
await view.webContents.session.cookies.set(cookie); await view.webContents.session.cookies.set(cookiesSetDetails);
} }
}; };
removeCookie = async (url: string, name: string) => {
const views = this.allViews;
if (!views) {
return;
}
logger.info('removing cookie from main window view(s)', { url, name });
for (const view of views) {
await view.webContents.session.cookies.remove(url, name);
}
};
getCookie = (url?: string, name?: string) => {
// all webviews share the same session
const view = this.allViews?.at(0);
if (!view) {
return;
}
return view.webContents.session.cookies.get({
url,
name,
});
};
getViewById = (id: string) => { getViewById = (id: string) => {
if (id === 'shell') { if (id === 'shell') {
return this.shellView; return this.shellView;
@@ -865,12 +861,8 @@ export async function setCookie(
return WebContentViewsManager.instance.setCookie(details); return WebContentViewsManager.instance.setCookie(details);
} }
export async function removeCookie(url: string, name: string): Promise<void> { export function getCookies() {
return WebContentViewsManager.instance.removeCookie(url, name); return WebContentViewsManager.instance.cookies;
}
export async function getCookie(url?: string, name?: string) {
return WebContentViewsManager.instance.getCookie(url, name);
} }
// there is no proper way to listen to webContents resize event // there is no proper way to listen to webContents resize event