Skip to content

Commit d7fd833

Browse files
committed
Skip push requests when the browser is offline
When `navigator.onLine` reports that the browser is offline, reject the push in `PushApi#push` before calling the transport. The existing catch paths in `Appsignal#send` and the `Dispatcher` retry loop treat this the same as any other push failure, so the span is queued and the dispatcher keeps backing off until connectivity returns. This prevents runaway fetch attempts when a flood of errors is generated without network connectivity, which was observed to crash browser tabs when combined with `@appsignal/plugin-window-events`. Fixes: appsignal/support#338
1 parent 40bfeef commit d7fd833

4 files changed

Lines changed: 38 additions & 1 deletion

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
bump: patch
3+
type: fix
4+
---
5+
6+
Skip push requests when the browser is offline.
7+
8+
When `navigator.onLine` reports that the browser is offline, the Push API client no longer attempts to send spans. The request is rejected before hitting the transport, which stops runaway retries that could crash tabs when a flood of errors was produced without connectivity.

packages/javascript/src/__tests__/api.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,21 @@ describe("PushApi", () => {
3636
body: FIXTURE.toJSON()
3737
})
3838
})
39+
40+
it("rejects without making a request when the browser is offline", async () => {
41+
Object.defineProperty(window.navigator, "onLine", {
42+
configurable: true,
43+
get: () => false
44+
})
45+
46+
fetchMock.mockClear()
47+
await expect(api.push(FIXTURE)).rejects.toBeUndefined()
48+
expect(fetchMock).not.toHaveBeenCalled()
49+
50+
Object.defineProperty(window.navigator, "onLine", {
51+
configurable: true,
52+
get: () => true
53+
})
54+
})
3955
})
4056
})

packages/javascript/src/api.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Environment } from "./environment"
1+
import { Environment, isOffline } from "./environment"
22
import { Span } from "./span"
33

44
import { XDomainTransport } from "./transports/xdomain"
@@ -38,6 +38,9 @@ export class PushApi {
3838
* @return {Promise<Span>} A single API `Span`
3939
*/
4040
public async push(span: Span): Promise<Span> {
41+
if (isOffline()) {
42+
return Promise.reject()
43+
}
4144
await this._transport.send(span.toJSON())
4245
return span
4346
}

packages/javascript/src/environment.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,13 @@ export function getGlobalObject<T>(): T {
114114
? self
115115
: {}) as T
116116
}
117+
118+
/**
119+
* Returns `true` when the browser reports itself as offline via
120+
* `navigator.onLine`. Falls back to `false` when the property is
121+
* unavailable (e.g. in Node) so callers default to attempting a request.
122+
*/
123+
export function isOffline(): boolean {
124+
const globals = getGlobalObject<any>()
125+
return globals.navigator !== undefined && globals.navigator.onLine === false
126+
}

0 commit comments

Comments
 (0)