@@ -13,7 +13,7 @@ function reportError(error: Error | string) {
13
13
import { spawn , exec , ChildProcess } from 'child_process' ;
14
14
import * as os from 'os' ;
15
15
import * as path from 'path' ;
16
- import { app , BrowserWindow , shell , Menu } from 'electron' ;
16
+ import { app , BrowserWindow , shell , Menu , Notification } from 'electron' ;
17
17
18
18
import * as windowStateKeeper from 'electron-window-state' ;
19
19
@@ -165,6 +165,11 @@ app.on('web-contents-created', (_event, contents) => {
165
165
} ) ;
166
166
} ) ;
167
167
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
+
168
173
async function startServer ( retries = 2 ) {
169
174
const binName = isWindows ? 'httptoolkit-server.cmd' : 'httptoolkit-server' ;
170
175
const serverBinPath = path . join ( __dirname , '..' , 'httptoolkit-server' , 'bin' , binName ) ;
@@ -184,29 +189,55 @@ async function startServer(retries = 2) {
184
189
Sentry . addBreadcrumb ( { category : 'server-stdout' , message : data . toString ( 'utf8' ) , level : < any > 'info' } ) ;
185
190
} ) ;
186
191
192
+ let lastError : string | undefined = undefined ;
187
193
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 ] ;
189
202
} ) ;
190
203
204
+ const serverStartTime = Date . now ( ) ;
205
+
191
206
const serverShutdown : Promise < void > = new Promise < Error | number | null > ( ( resolve ) => {
192
207
server ! . once ( 'error' , resolve ) ;
193
208
server ! . once ( 'exit' , resolve ) ;
194
209
} ) . then ( ( errorOrCode ) => {
195
210
if ( serverKilled ) return ;
196
211
197
212
// 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
+ }
198
224
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 ) ;
201
227
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
+ ) ;
204
232
233
+ // Retry limited times, but not for near-immediate failures.
234
+ if ( retries > 0 && serverRunTime > 5000 ) {
205
235
// This will break the app, so refresh it
206
236
if ( mainWindow ) mainWindow . reload ( ) ;
207
237
return startServer ( retries - 1 ) ;
208
238
}
209
239
240
+ // If we've run out of retries, throw (kill the app entirely)
210
241
throw error ;
211
242
} ) ;
212
243
@@ -223,7 +254,6 @@ if (require('electron-squirrel-startup')) {
223
254
setTimeout ( ( ) => app . quit ( ) , 500 ) ;
224
255
} else {
225
256
startServer ( ) . catch ( ( err ) => {
226
- reportError ( err ) ;
227
257
console . error ( 'Failed to start server, exiting.' , err ) ;
228
258
229
259
// Hide immediately, shutdown entirely after a brief pause for Sentry
0 commit comments