Skip to content

Commit

Permalink
feat(replay): Add a new option networkDetailDenyUrls (#8439)
Browse files Browse the repository at this point in the history
  • Loading branch information
ziyad-elabid-nw committed Jul 4, 2023
1 parent 1db809b commit 9ede8a3
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 7 deletions.
10 changes: 8 additions & 2 deletions packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ export function handleNetworkBreadcrumbs(replay: ReplayContainer): void {
try {
const textEncoder = new TextEncoder();

const { networkDetailAllowUrls, networkCaptureBodies, networkRequestHeaders, networkResponseHeaders } =
replay.getOptions();
const {
networkDetailAllowUrls,
networkDetailDenyUrls,
networkCaptureBodies,
networkRequestHeaders,
networkResponseHeaders,
} = replay.getOptions();

const options: ExtendedNetworkBreadcrumbsOptions = {
replay,
textEncoder,
networkDetailAllowUrls,
networkDetailDenyUrls,
networkCaptureBodies,
networkRequestHeaders,
networkResponseHeaders,
Expand Down
3 changes: 2 additions & 1 deletion packages/replay/src/coreHandlers/util/fetchUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ async function _prepareFetchData(
response_body_size: responseBodySize,
} = breadcrumb.data;

const captureDetails = urlMatches(url, options.networkDetailAllowUrls);
const captureDetails =
urlMatches(url, options.networkDetailAllowUrls) && !urlMatches(url, options.networkDetailDenyUrls);

const request = captureDetails
? _getRequestInfo(options, hint.input, requestBodySize)
Expand Down
2 changes: 1 addition & 1 deletion packages/replay/src/coreHandlers/util/xhrUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function _prepareXhrData(
return null;
}

if (!urlMatches(url, options.networkDetailAllowUrls)) {
if (!urlMatches(url, options.networkDetailAllowUrls) || urlMatches(url, options.networkDetailDenyUrls)) {
const request = buildSkippedNetworkRequestOrResponse(requestBodySize);
const response = buildSkippedNetworkRequestOrResponse(responseBodySize);
return {
Expand Down
2 changes: 2 additions & 0 deletions packages/replay/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class Replay implements Integration {
slowClickIgnoreSelectors = [],

networkDetailAllowUrls = [],
networkDetailDenyUrls = [],
networkCaptureBodies = true,
networkRequestHeaders = [],
networkResponseHeaders = [],
Expand Down Expand Up @@ -138,6 +139,7 @@ export class Replay implements Integration {
slowClickTimeout,
slowClickIgnoreSelectors,
networkDetailAllowUrls,
networkDetailDenyUrls,
networkCaptureBodies,
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
networkResponseHeaders: _getMergedNetworkHeaders(networkResponseHeaders),
Expand Down
15 changes: 12 additions & 3 deletions packages/replay/src/types/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,32 @@ export interface ReplayNetworkOptions {
*/
networkDetailAllowUrls: (string | RegExp)[];

/**
* Deny request/response details for XHR/Fetch requests that match the given URLs.
* The URLs can be strings or regular expressions.
* When provided a string, we will deny any URL that contains the given string.
* You can use a Regex to handle exact matches or more complex matching.
* URLs matching these patterns will not have bodies & additional headers captured.
*/
networkDetailDenyUrls: (string | RegExp)[];

/**
* If request & response bodies should be captured.
* Only applies to URLs matched by `networkDetailAllowUrls`.
* Only applies to URLs matched by `networkDetailAllowUrls` and not matched by `networkDetailDenyUrls`.
* Defaults to true.
*/
networkCaptureBodies: boolean;

/**
* Capture the following request headers, in addition to the default ones.
* Only applies to URLs matched by `networkDetailAllowUrls`.
* Only applies to URLs matched by `networkDetailAllowUrls` and not matched by `networkDetailDenyUrls`.
* Any headers defined here will be captured in addition to the default headers.
*/
networkRequestHeaders: string[];

/**
* Capture the following response headers, in addition to the default ones.
* Only applies to URLs matched by `networkDetailAllowUrls`.
* Only applies to URLs matched by `networkDetailAllowUrls` and not matched by `networkDetailDenyUrls`.
* Any headers defined here will be captured in addition to the default headers.
*/
networkResponseHeaders: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
textEncoder: new TextEncoder(),
replay: setupReplayContainer(),
networkDetailAllowUrls: ['https://example.com'],
networkDetailDenyUrls: ['http://localhost:8080'],
networkCaptureBodies: false,
networkRequestHeaders: ['content-type', 'accept', 'x-custom-header'],
networkResponseHeaders: ['content-type', 'accept', 'x-custom-header'],
Expand Down Expand Up @@ -1382,5 +1383,166 @@ other-header: test`;
]);
});
});

describe.each([
['exact string match', 'https://example.com/foo'],
['partial string match', 'https://example.com/bar/what'],
['exact regex match', 'http://example.com/exact'],
['partial regex match', 'http://example.com/partial/string'],
])('matching URL %s', (_label, url) => {
it('correctly deny URL for fetch request', async () => {
options.networkDetailDenyUrls = [
'https://example.com/foo',
'com/bar',
/^http:\/\/example.com\/exact$/,
/^http:\/\/example.com\/partial/,
];

const breadcrumb: Breadcrumb = {
category: 'fetch',
data: {
method: 'GET',
url,
status_code: 200,
},
};

const mockResponse = getMockResponse('13', 'test response');

const hint: FetchBreadcrumbHint = {
input: ['GET', { body: 'test input' }],
response: mockResponse,
startTimestamp: BASE_TIMESTAMP + 1000,
endTimestamp: BASE_TIMESTAMP + 2000,
};
beforeAddNetworkBreadcrumb(options, breadcrumb, hint);

expect(breadcrumb).toEqual({
category: 'fetch',
data: {
method: 'GET',
request_body_size: 10,
response_body_size: 13,
status_code: 200,
url,
},
});

await waitForReplayEventBuffer();

expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([
{
data: {
payload: {
data: {
method: 'GET',
request: {
_meta: {
warnings: ['URL_SKIPPED'],
},
headers: {},
size: 10,
},
response: {
_meta: {
warnings: ['URL_SKIPPED'],
},
headers: {},
size: 13,
},
statusCode: 200,
},
description: url,
endTimestamp: (BASE_TIMESTAMP + 2000) / 1000,
op: 'resource.fetch',
startTimestamp: (BASE_TIMESTAMP + 1000) / 1000,
},
tag: 'performanceSpan',
},
timestamp: (BASE_TIMESTAMP + 1000) / 1000,
type: 5,
},
]);
});

it('correctly deny URL for xhr request', async () => {
options.networkDetailDenyUrls = [
'https://example.com/foo',
'com/bar',
/^http:\/\/example.com\/exact$/,
/^http:\/\/example.com\/partial/,
];

const breadcrumb: Breadcrumb = {
category: 'xhr',
data: {
method: 'GET',
url,
status_code: 200,
},
};
const xhr = new XMLHttpRequest();
Object.defineProperty(xhr, 'response', {
value: 'test response',
});
Object.defineProperty(xhr, 'responseText', {
value: 'test response',
});
const hint: XhrBreadcrumbHint = {
xhr,
input: 'test input',
startTimestamp: BASE_TIMESTAMP + 1000,
endTimestamp: BASE_TIMESTAMP + 2000,
};
beforeAddNetworkBreadcrumb(options, breadcrumb, hint);

expect(breadcrumb).toEqual({
category: 'xhr',
data: {
method: 'GET',
request_body_size: 10,
response_body_size: 13,
status_code: 200,
url,
},
});

await waitForReplayEventBuffer();

expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([
{
data: {
payload: {
data: {
method: 'GET',
request: {
_meta: {
warnings: ['URL_SKIPPED'],
},
headers: {},
size: 10,
},
response: {
_meta: {
warnings: ['URL_SKIPPED'],
},
headers: {},
size: 13,
},
statusCode: 200,
},
description: url,
endTimestamp: (BASE_TIMESTAMP + 2000) / 1000,
op: 'resource.xhr',
startTimestamp: (BASE_TIMESTAMP + 1000) / 1000,
},
tag: 'performanceSpan',
},
timestamp: (BASE_TIMESTAMP + 1000) / 1000,
type: 5,
},
]);
});
});
});
});
1 change: 1 addition & 0 deletions packages/replay/test/utils/setupReplayContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const DEFAULT_OPTIONS = {
useCompression: false,
blockAllMedia: true,
networkDetailAllowUrls: [],
networkDetailDenyUrls: [],
networkCaptureBodies: true,
networkRequestHeaders: [],
networkResponseHeaders: [],
Expand Down

0 comments on commit 9ede8a3

Please sign in to comment.