@@ -13,7 +13,7 @@ function reportError(error: Error | string) {
1313import { spawn , exec , ChildProcess } from 'child_process' ;
1414import * as os from 'os' ;
1515import * as path from 'path' ;
16- import { app , BrowserWindow , shell , Menu } from 'electron' ;
16+ import { app , BrowserWindow , shell , Menu , Notification } from 'electron' ;
1717
1818import * as windowStateKeeper from 'electron-window-state' ;
1919
@@ -165,6 +165,11 @@ app.on('web-contents-created', (_event, contents) => {
165165 } ) ;
166166} ) ;
167167
168+ function showNotification ( title : string , body : string ) {
169+ const notification = new Notification ( { title, body, icon : path . join ( __dirname , 'src' , 'icon.png' ) } ) ;
170+ notification . show ( ) ;
171+ }
172+
168173async function startServer ( retries = 2 ) {
169174 const binName = isWindows ? 'httptoolkit-server.cmd' : 'httptoolkit-server' ;
170175 const serverBinPath = path . join ( __dirname , '..' , 'httptoolkit-server' , 'bin' , binName ) ;
@@ -184,29 +189,55 @@ async function startServer(retries = 2) {
184189 Sentry . addBreadcrumb ( { category : 'server-stdout' , message : data . toString ( 'utf8' ) , level : < any > 'info' } ) ;
185190 } ) ;
186191
192+ let lastError : string | undefined = undefined ;
187193 server . stderr . on ( 'data' , ( data ) => {
188- Sentry . addBreadcrumb ( { category : 'server-stderr' , message : data . toString ( 'utf8' ) , level : < any > 'warning' } ) ;
194+ const errorOutput = data . toString ( 'utf8' ) ;
195+ Sentry . addBreadcrumb ( { category : 'server-stderr' , message : errorOutput , level : < any > 'warning' } ) ;
196+
197+ // Remember the last '*Error:' line we saw.
198+ lastError = errorOutput
199+ . split ( '\n' )
200+ . filter ( ( line : string ) => line . match ( / ^ \w * E r r o r : / ) )
201+ . slice ( - 1 ) [ 0 ] ;
189202 } ) ;
190203
204+ const serverStartTime = Date . now ( ) ;
205+
191206 const serverShutdown : Promise < void > = new Promise < Error | number | null > ( ( resolve ) => {
192207 server ! . once ( 'error' , resolve ) ;
193208 server ! . once ( 'exit' , resolve ) ;
194209 } ) . then ( ( errorOrCode ) => {
195210 if ( serverKilled ) return ;
196211
197212 // The server should never shutdown unless the whole process is finished, so this is bad.
213+ const serverRunTime = Date . now ( ) - serverStartTime ;
214+
215+ let error : Error ;
216+
217+ if ( errorOrCode && typeof errorOrCode !== 'number' ) {
218+ error = errorOrCode ;
219+ } else if ( lastError ) {
220+ error = new Error ( `Server crashed with '${ lastError } ' (${ errorOrCode } )` ) ;
221+ } else {
222+ error = new Error ( `Server shutdown unexpectedly with code ${ errorOrCode } ` ) ;
223+ }
198224
199- const error = errorOrCode && typeof errorOrCode !== 'number' ?
200- errorOrCode : new Error ( `Server shutdown unexpectedly with code ${ errorOrCode } .` ) ;
225+ Sentry . addBreadcrumb ( { category : 'server-exit' , message : error . message , level : < any > 'error' , data : { serverRunTime } } ) ;
226+ reportError ( error ) ;
201227
202- if ( retries > 0 ) {
203- Sentry . addBreadcrumb ( { category : 'server-exit' , message : error . message , level : < any > 'error' } ) ;
228+ showNotification (
229+ 'HTTP Toolkit crashed' ,
230+ `${ error . message } . Please file an issue at github.com/httptoolkit/feedback.`
231+ ) ;
204232
233+ // Retry limited times, but not for near-immediate failures.
234+ if ( retries > 0 && serverRunTime > 5000 ) {
205235 // This will break the app, so refresh it
206236 if ( mainWindow ) mainWindow . reload ( ) ;
207237 return startServer ( retries - 1 ) ;
208238 }
209239
240+ // If we've run out of retries, throw (kill the app entirely)
210241 throw error ;
211242 } ) ;
212243
@@ -223,7 +254,6 @@ if (require('electron-squirrel-startup')) {
223254 setTimeout ( ( ) => app . quit ( ) , 500 ) ;
224255} else {
225256 startServer ( ) . catch ( ( err ) => {
226- reportError ( err ) ;
227257 console . error ( 'Failed to start server, exiting.' , err ) ;
228258
229259 // Hide immediately, shutdown entirely after a brief pause for Sentry
0 commit comments