(
options: BrowserTransportOptions,
nativeFetch: typeof WINDOW.fetch = getNativeImplementation('fetch'),
)
| 10 | * Creates a Transport that uses the Fetch API to send events to Sentry. |
| 11 | */ |
| 12 | export function makeFetchTransport( |
| 13 | options: BrowserTransportOptions, |
| 14 | nativeFetch: typeof WINDOW.fetch = getNativeImplementation('fetch'), |
| 15 | ): Transport { |
| 16 | let pendingBodySize = 0; |
| 17 | let pendingCount = 0; |
| 18 | |
| 19 | async function makeRequest(request: TransportRequest): Promise<TransportMakeRequestResponse> { |
| 20 | const requestSize = request.body.length; |
| 21 | pendingBodySize += requestSize; |
| 22 | pendingCount++; |
| 23 | |
| 24 | const requestOptions: RequestInit = { |
| 25 | body: request.body, |
| 26 | method: 'POST', |
| 27 | referrerPolicy: 'strict-origin', |
| 28 | headers: options.headers, |
| 29 | // Outgoing requests are usually cancelled when navigating to a different page, causing a "TypeError: Failed to |
| 30 | // fetch" error and sending a "network_error" client-outcome - in Chrome, the request status shows "(cancelled)". |
| 31 | // The `keepalive` flag keeps outgoing requests alive, even when switching pages. We want this since we're |
| 32 | // frequently sending events right before the user is switching pages (eg. when finishing navigation transactions). |
| 33 | // Gotchas: |
| 34 | // - `keepalive` isn't supported by Firefox |
| 35 | // - As per spec (https://fetch.spec.whatwg.org/#http-network-or-cache-fetch): |
| 36 | // If the sum of contentLength and inflightKeepaliveBytes is greater than 64 kibibytes, then return a network error. |
| 37 | // We will therefore only activate the flag when we're below that limit. |
| 38 | // There is also a limit of requests that can be open at the same time, so we also limit this to 15 |
| 39 | // See https://github.com/getsentry/sentry-javascript/pull/7553 for details |
| 40 | keepalive: pendingBodySize <= 60_000 && pendingCount < 15, |
| 41 | ...options.fetchOptions, |
| 42 | }; |
| 43 | |
| 44 | try { |
| 45 | // Note: We do not need to suppress tracing here, because we are using the native fetch, instead of our wrapped one. |
| 46 | const response = await nativeFetch(options.url, requestOptions); |
| 47 | |
| 48 | return { |
| 49 | statusCode: response.status, |
| 50 | headers: { |
| 51 | 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), |
| 52 | 'retry-after': response.headers.get('Retry-After'), |
| 53 | }, |
| 54 | }; |
| 55 | } catch (e) { |
| 56 | clearCachedImplementation('fetch'); |
| 57 | throw e; |
| 58 | } finally { |
| 59 | pendingBodySize -= requestSize; |
| 60 | pendingCount--; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | return createTransport( |
| 65 | options, |
| 66 | makeRequest, |
| 67 | makePromiseBuffer(options.bufferSize || DEFAULT_BROWSER_TRANSPORT_BUFFER_SIZE), |
| 68 | ); |
| 69 | } |
no test coverage detected