Skip to content

Commit 0a81c64

Browse files
committed
add meta & ref
1 parent 75ace7d commit 0a81c64

File tree

6 files changed

+163
-67
lines changed

6 files changed

+163
-67
lines changed

packages/replay/src/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ export const ERROR_CHECKOUT_TIME = 60_000;
2929
export const RETRY_BASE_INTERVAL = 5000;
3030
export const RETRY_MAX_COUNT = 3;
3131

32-
/* The max size in bytes of a network body. Any body larger than this will be dropped. */
33-
export const NETWORK_BODY_MAX_SIZE = 50_000;
32+
/* The max (uncompressed) size in bytes of a network body. Any body larger than this will be dropped. */
33+
export const NETWORK_BODY_MAX_SIZE = 300_000;

packages/replay/src/coreHandlers/util/fetchUtils.ts

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import type { Breadcrumb, FetchBreadcrumbData, TextEncoderInternal } from '@sentry/types';
2-
import { dropUndefinedKeys, logger } from '@sentry/utils';
3-
4-
import { NETWORK_BODY_MAX_SIZE } from '../../constants';
5-
import type { FetchHint, NetworkBody, ReplayContainer, ReplayNetworkRequestData } from '../../types';
2+
import { logger } from '@sentry/utils';
3+
4+
import type {
5+
FetchHint,
6+
NetworkBody,
7+
ReplayContainer,
8+
ReplayNetworkRequestData,
9+
ReplayNetworkRequestOrResponse,
10+
} from '../../types';
611
import { addNetworkBreadcrumb } from './addNetworkBreadcrumb';
712
import {
13+
buildNetworkRequestOrResponse,
814
getBodySize,
915
getBodyString,
1016
getNetworkBody,
@@ -63,40 +69,16 @@ async function _prepareFetchData(
6369
): Promise<ReplayNetworkRequestData> {
6470
const { startTimestamp, endTimestamp } = hint;
6571

66-
const { url, method, status_code: statusCode, request_body_size: requestBodySize } = breadcrumb.data;
67-
let responseBodySize = breadcrumb.data.response_body_size;
68-
let responseBody: NetworkBody | undefined;
69-
let requestBody: NetworkBody | undefined;
70-
71-
// Only clone the response if we need to
72-
if (options.captureBodies || responseBodySize === undefined) {
73-
try {
74-
// We have to clone this, as the body can only be read once
75-
const response = hint.response.clone();
76-
const { body, bodyText } = await _parseFetchBody(response);
77-
78-
if (bodyText && bodyText.length && responseBodySize === undefined) {
79-
responseBodySize = getBodySize(bodyText, options.textEncoder);
80-
}
81-
82-
if (options.captureBodies && responseBodySize && responseBodySize < NETWORK_BODY_MAX_SIZE) {
83-
responseBody = body;
84-
}
85-
} catch {
86-
// just ignore if something fails here
87-
}
88-
}
89-
90-
if (options.captureBodies && requestBodySize && requestBodySize < NETWORK_BODY_MAX_SIZE) {
91-
// We only want to transmit string or string-like bodies
92-
const body = _getFetchRequestArgBody(hint.input);
93-
requestBody = getNetworkBody(getBodyString(body));
94-
}
72+
const {
73+
url,
74+
method,
75+
status_code: statusCode,
76+
request_body_size: requestBodySize,
77+
response_body_size: responseBodySize,
78+
} = breadcrumb.data;
9579

96-
const request =
97-
requestBody || requestBodySize ? dropUndefinedKeys({ body: requestBody, size: requestBodySize }) : undefined;
98-
const response =
99-
responseBody || responseBodySize ? dropUndefinedKeys({ body: responseBody, size: responseBodySize }) : undefined;
80+
const request = _getRequestInfo(options, hint.input, requestBodySize);
81+
const response = await _getResponseInfo(options, hint.response, responseBodySize);
10082

10183
return {
10284
startTimestamp,
@@ -109,6 +91,52 @@ async function _prepareFetchData(
10991
};
11092
}
11193

94+
function _getRequestInfo(
95+
{ captureBodies }: { captureBodies: boolean },
96+
input: FetchHint['input'],
97+
requestBodySize?: number,
98+
): ReplayNetworkRequestOrResponse | undefined {
99+
if (!captureBodies) {
100+
return buildNetworkRequestOrResponse(requestBodySize, undefined);
101+
}
102+
103+
// We only want to transmit string or string-like bodies
104+
const requestBody = _getFetchRequestArgBody(input);
105+
const body = getNetworkBody(getBodyString(requestBody));
106+
return buildNetworkRequestOrResponse(requestBodySize, body);
107+
}
108+
109+
async function _getResponseInfo(
110+
{ captureBodies, textEncoder }: { captureBodies: boolean; textEncoder: TextEncoderInternal },
111+
response: Response,
112+
responseBodySize?: number,
113+
): Promise<ReplayNetworkRequestOrResponse | undefined> {
114+
if (!captureBodies && responseBodySize !== undefined) {
115+
return buildNetworkRequestOrResponse(responseBodySize, undefined);
116+
}
117+
118+
// Only clone the response if we need to
119+
try {
120+
// We have to clone this, as the body can only be read once
121+
const res = response.clone();
122+
const { body, bodyText } = await _parseFetchBody(res);
123+
124+
const size =
125+
bodyText && bodyText.length && responseBodySize === undefined
126+
? getBodySize(bodyText, textEncoder)
127+
: responseBodySize;
128+
129+
if (captureBodies) {
130+
return buildNetworkRequestOrResponse(size, body);
131+
}
132+
133+
return buildNetworkRequestOrResponse(size, undefined);
134+
} catch {
135+
// fallback
136+
return buildNetworkRequestOrResponse(responseBodySize, undefined);
137+
}
138+
}
139+
112140
async function _parseFetchBody(
113141
response: Response,
114142
): Promise<{ body?: NetworkBody | undefined; bodyText?: string | undefined }> {

packages/replay/src/coreHandlers/util/networkUtils.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import type { TextEncoderInternal } from '@sentry/types';
22
import { dropUndefinedKeys } from '@sentry/utils';
33

4-
import type { NetworkBody, ReplayNetworkRequestData, ReplayPerformanceEntry } from '../../types';
4+
import { NETWORK_BODY_MAX_SIZE } from '../../constants';
5+
import type {
6+
NetworkBody,
7+
ReplayNetworkRequestData,
8+
ReplayNetworkRequestOrResponse,
9+
ReplayPerformanceEntry,
10+
} from '../../types';
511

612
/** Get the size of a body. */
713
export function getBodySize(
@@ -111,6 +117,57 @@ export function getNetworkBody(bodyText: string | undefined): NetworkBody | unde
111117
return bodyText;
112118
}
113119

120+
/** Build the request or response part of a replay network breadcrumb. */
121+
export function buildNetworkRequestOrResponse(
122+
bodySize: number | undefined,
123+
body: NetworkBody | undefined,
124+
): ReplayNetworkRequestOrResponse | undefined {
125+
if (!bodySize) {
126+
return undefined;
127+
}
128+
129+
if (!body) {
130+
return {
131+
size: bodySize,
132+
};
133+
}
134+
135+
const info: ReplayNetworkRequestOrResponse = {
136+
size: bodySize,
137+
};
138+
139+
if (bodySize < NETWORK_BODY_MAX_SIZE) {
140+
info.body = body;
141+
} else {
142+
info._meta = {
143+
errors: ['MAX_BODY_SIZE_EXCEEDED'],
144+
};
145+
}
146+
147+
return info;
148+
}
149+
150+
/**
151+
* Set the network body if it doesn't exceed the max size.
152+
*/
153+
export function setReplayNetworkRequestNetworkBody(
154+
data: ReplayNetworkRequestOrResponse,
155+
body: NetworkBody | undefined,
156+
bodySize: number | undefined,
157+
): void {
158+
if (!body || !bodySize) {
159+
return;
160+
}
161+
162+
if (bodySize < NETWORK_BODY_MAX_SIZE) {
163+
data.body = body;
164+
} else {
165+
data._meta = {
166+
errors: ['MAX_BODY_SIZE_EXCEEDED'],
167+
};
168+
}
169+
}
170+
114171
function _serializeFormData(formData: FormData): string {
115172
// This is a bit simplified, but gives us a decent estimate
116173
// This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13'

packages/replay/src/coreHandlers/util/xhrUtils.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { Breadcrumb, TextEncoderInternal, XhrBreadcrumbData } from '@sentry/types';
22
import { logger } from '@sentry/utils';
33

4-
import { NETWORK_BODY_MAX_SIZE } from '../../constants';
5-
import type { NetworkBody, ReplayContainer, ReplayNetworkRequestData, XhrHint } from '../../types';
4+
import type { ReplayContainer, ReplayNetworkRequestData, XhrHint } from '../../types';
65
import { addNetworkBreadcrumb } from './addNetworkBreadcrumb';
76
import {
7+
buildNetworkRequestOrResponse,
88
getBodySize,
99
getBodyString,
1010
getNetworkBody,
@@ -71,25 +71,19 @@ function _prepareXhrData(
7171
request_body_size: requestBodySize,
7272
response_body_size: responseBodySize,
7373
} = breadcrumb.data;
74-
let responseBody: NetworkBody | undefined;
75-
let requestBody: NetworkBody | undefined;
76-
77-
if (options.captureBodies) {
78-
if (requestBodySize && requestBodySize < NETWORK_BODY_MAX_SIZE) {
79-
requestBody = getNetworkBody(getBodyString(input));
80-
}
81-
82-
if (responseBodySize && responseBodySize < NETWORK_BODY_MAX_SIZE) {
83-
responseBody = getNetworkBody(hint.xhr.responseText);
84-
}
85-
}
8674

8775
if (!url) {
8876
return null;
8977
}
9078

91-
const request = requestBody || requestBodySize ? { body: requestBody, size: requestBodySize } : undefined;
92-
const response = responseBody || responseBodySize ? { body: responseBody, size: responseBodySize } : undefined;
79+
const request = buildNetworkRequestOrResponse(
80+
requestBodySize,
81+
options.captureBodies ? getNetworkBody(getBodyString(input)) : undefined,
82+
);
83+
const response = buildNetworkRequestOrResponse(
84+
responseBodySize,
85+
options.captureBodies ? getNetworkBody(hint.xhr.responseText) : undefined,
86+
);
9387

9488
return {
9589
startTimestamp,

packages/replay/src/types.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,20 +367,24 @@ export type FetchHint = FetchBreadcrumbHint & {
367367

368368
export type NetworkBody = Record<string, unknown> | string;
369369

370+
type NetworkMetaError = 'MAX_BODY_SIZE_EXCEEDED';
371+
372+
interface NetworkMeta {
373+
errors?: NetworkMetaError[];
374+
}
375+
376+
export interface ReplayNetworkRequestOrResponse {
377+
size?: number;
378+
body?: NetworkBody;
379+
_meta?: NetworkMeta;
380+
}
381+
370382
export type ReplayNetworkRequestData = {
371383
startTimestamp: number;
372384
endTimestamp: number;
373385
url: string;
374386
method?: string;
375387
statusCode: number;
376-
377-
request?: {
378-
size?: number;
379-
body?: NetworkBody;
380-
};
381-
382-
response?: {
383-
size?: number;
384-
body?: NetworkBody;
385-
};
388+
request?: ReplayNetworkRequestOrResponse;
389+
response?: ReplayNetworkRequestOrResponse;
386390
};

packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import { setupReplayContainer } from '../../utils/setupReplayContainer';
1717
jest.useFakeTimers();
1818

1919
async function waitForReplayEventBuffer() {
20+
// Need one Promise.resolve() per await in the util functions
21+
await Promise.resolve();
2022
await Promise.resolve();
21-
jest.runAllTimers();
2223
await Promise.resolve();
2324
}
2425

@@ -609,9 +610,15 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
609610
statusCode: 200,
610611
request: {
611612
size: LARGE_BODY.length,
613+
_meta: {
614+
errors: ['MAX_BODY_SIZE_EXCEEDED'],
615+
},
612616
},
613617
response: {
614618
size: LARGE_BODY.length,
619+
_meta: {
620+
errors: ['MAX_BODY_SIZE_EXCEEDED'],
621+
},
615622
},
616623
},
617624
description: 'https://example.com',
@@ -869,9 +876,15 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
869876
statusCode: 200,
870877
request: {
871878
size: LARGE_BODY.length,
879+
_meta: {
880+
errors: ['MAX_BODY_SIZE_EXCEEDED'],
881+
},
872882
},
873883
response: {
874884
size: LARGE_BODY.length,
885+
_meta: {
886+
errors: ['MAX_BODY_SIZE_EXCEEDED'],
887+
},
875888
},
876889
},
877890
description: 'https://example.com',

0 commit comments

Comments
 (0)