Skip to content

test: Add test utility to intercept requests to Sentry #7271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 24, 2023

Conversation

lforst
Copy link
Contributor

@lforst lforst commented Feb 23, 2023

This PR adds a package containing utilities to start a proxy server that is able to intercept requests to sentry (via the tunnel option) and additionally provides utility functions to listen for particular envelopes/events.

Resolves #7263

@lforst lforst changed the title test: Add test utility to intercept sentry requests test: Add test utility to intercept requests to Sentry Feb 23, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Feb 23, 2023

size-limit report 📦

Path Size
@sentry/browser - ES5 CDN Bundle (gzipped + minified) 20.11 KB (+0.21% 🔺)
@sentry/browser - ES5 CDN Bundle (minified) 62.49 KB (+0.31% 🔺)
@sentry/browser - ES6 CDN Bundle (gzipped + minified) 18.74 KB (+0.28% 🔺)
@sentry/browser - ES6 CDN Bundle (minified) 55.5 KB (+0.35% 🔺)
@sentry/browser - Webpack (gzipped + minified) 20.48 KB (+0.35% 🔺)
@sentry/browser - Webpack (minified) 66.94 KB (+0.28% 🔺)
@sentry/react - Webpack (gzipped + minified) 20.51 KB (+0.33% 🔺)
@sentry/nextjs Client - Webpack (gzipped + minified) 48.05 KB (+0.41% 🔺)
@sentry/browser + @sentry/tracing - ES5 CDN Bundle (gzipped + minified) 27.04 KB (+0.38% 🔺)
@sentry/browser + @sentry/tracing - ES6 CDN Bundle (gzipped + minified) 25.29 KB (+0.34% 🔺)
@sentry/replay ES6 CDN Bundle (gzipped + minified) 42.78 KB (+0.4% 🔺)
@sentry/replay - Webpack (gzipped + minified) 36.83 KB (+0.01% 🔺)
@sentry/browser + @sentry/tracing + @sentry/replay - ES6 CDN Bundle (gzipped + minified) 60.34 KB (+0.17% 🔺)
@sentry/browser + @sentry/replay - ES6 CDN Bundle (gzipped + minified) 53.9 KB (+0.12% 🔺)

@github-actions
Copy link
Contributor

Replay SDK metrics 🚀

    Plain +Sentry +Replay
Revision Value Value Diff Ratio Value Diff Ratio
LCP This PR 8e27ff9 76.08 ms 99.51 ms +23.43 ms +30.80 % 128.24 ms +52.17 ms +68.57 %
Previous e60cd02 95.19 ms 116.88 ms +21.69 ms +22.79 % 151.44 ms +56.25 ms +59.09 %
CLS This PR 8e27ff9 0.06 ms 0.06 ms -0.00 ms -0.04 % 0.06 ms +0.00 ms +0.06 %
Previous e60cd02 0.06 ms 0.06 ms -0.00 ms -0.45 % 0.06 ms -0.00 ms -0.37 %
CPU This PR 8e27ff9 17.03 % 17.31 % +0.28 pp +1.64 % 23.65 % +6.62 pp +38.85 %
Previous e60cd02 18.74 % 17.90 % -0.85 pp -4.52 % 25.06 % +6.32 pp +33.70 %
JS heap avg This PR 8e27ff9 1.94 MB 1.99 MB +48.41 kB +2.49 % 2.87 MB +927.93 kB +47.77 %
Previous e60cd02 1.94 MB 2 MB +62.53 kB +3.22 % 2.87 MB +927.44 kB +47.82 %
JS heap max This PR 8e27ff9 2.31 MB 2.55 MB +247.86 kB +10.75 % 3.35 MB +1.05 MB +45.44 %
Previous e60cd02 2.3 MB 2.57 MB +265.21 kB +11.52 % 3.37 MB +1.06 MB +46.20 %
netTx This PR 8e27ff9 0 B 0 B 0 B n/a 2.22 kB +2.22 kB n/a
Previous e60cd02 0 B 0 B 0 B n/a 2.21 kB +2.21 kB n/a
netRx This PR 8e27ff9 0 B 0 B 0 B n/a 41 B +41 B n/a
Previous e60cd02 0 B 0 B 0 B n/a 41 B +41 B n/a
netCount This PR 8e27ff9 0 0 0 n/a 1 +1 n/a
Previous e60cd02 0 0 0 n/a 1 +1 n/a
netTime This PR 8e27ff9 0.00 ms 0.00 ms 0.00 ms n/a 64.27 ms +64.27 ms n/a
Previous e60cd02 0.00 ms 0.00 ms 0.00 ms n/a 117.55 ms +117.55 ms n/a

Previous results on branch: develop

RevisionLCPCLSCPUJS heap avgJS heap maxnetTxnetRxnetCountnetTime
e60cd02+56.25 ms-0.00 ms+6.32 pp+927.44 kB+1.06 MB+2.21 kB+41 B+1+117.55 ms
e25c067+48.34 ms+0.00 ms+5.59 pp+926.37 kB+1.05 MB+2.22 kB+41 B+1+65.23 ms
b1b249b+43.88 ms+0.00 ms+4.80 pp+937.99 kB+1.05 MB+2.22 kB+41 B+1+111.56 ms
12e34d4+28.57 ms+0.00 ms+5.77 pp+930.12 kB+1.04 MB+2.26 kB+41 B+1+109.67 ms
c46c56c+65.45 ms-0.00 ms+5.38 pp+930.26 kB+1.07 MB+2.21 kB+41 B+1+91.29 ms
7f4c4ec+56.64 ms-0.00 ms+5.57 pp+927.42 kB+1.06 MB+2.21 kB+41 B+1+110.83 ms
00d2360+55.18 ms+0.00 ms+2.23 pp+934.14 kB+1.05 MB+2.22 kB+41 B+1+71.65 ms

*) pp - percentage points - an absolute difference between two percentages.
Last updated: Thu, 23 Feb 2023 14:42:24 GMT

@lforst lforst marked this pull request as ready for review February 23, 2023 17:56
@lforst lforst requested review from Lms24 and AbhiPrasad February 23, 2023 17:56
Comment on lines +59 to +91
const sentryRequest = https.request(
sentryIngestUrl,
{ headers: proxyRequest.headers, method: proxyRequest.method },
sentryResponse => {
sentryResponse.addListener('data', (chunk: Buffer) => {
proxyResponse.write(chunk, 'binary');
sentryResponseChunks.push(chunk);
});

sentryResponse.addListener('end', () => {
eventCallbackListeners.forEach(listener => {
const rawProxyRequestBody = Buffer.concat(proxyRequestChunks).toString();
const rawSentryResponseBody = Buffer.concat(sentryResponseChunks).toString();

const data: SentryRequestCallbackData = {
envelope: parseEnvelope(rawProxyRequestBody, new TextEncoder(), new TextDecoder()),
rawProxyRequestBody,
rawSentryResponseBody,
sentryResponseStatusCode: sentryResponse.statusCode,
};

listener(Buffer.from(JSON.stringify(data)).toString('base64'));
});
proxyResponse.end();
});

sentryResponse.addListener('error', err => {
throw err;
});

proxyResponse.writeHead(sentryResponse.statusCode || 500, sentryResponse.headers);
},
);

Check failure

Code scanning / CodeQL

Server-side request forgery

The [URL](1) of this request depends on a [user-provided value](2).
Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need both the eventCallbackServer and a proxyServer?

for (const envelopeItem of envelopeItems) {
if (callback(envelopeItem)) {
resolve(envelopeItem);
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: Why are we returning from the promise? Ditto for the other waitForX methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not returning from the promise, we're returning from the callback of waitForRequest. This is to stop listening for events. Do you know a better way to do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup you're right, the nesting just confused me.

I think it's fine, just takes a bit to get - this is the best way to share state this functional way.

The alternative would be to have this all be in a class mutating some internal state, but that is prob less clean.

proxyServerName: string,
callback: (eventData: SentryRequestCallbackData) => boolean,
): Promise<SentryRequestCallbackData> {
const tmpFileWithPort = path.join(os.tmpdir(), `event-proxy-server-${proxyServerName}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: If this is expected to keep in sync with tmpFileWithPort generated from eventCallbackServerStartupPromise, I would like it to be a helper func. This way there's no way that it'll ever differ unless done intentionally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 9090f79

callback: (eventData: SentryRequestCallbackData) => boolean,
): Promise<SentryRequestCallbackData> {
const tmpFileWithPort = path.join(os.tmpdir(), `event-proxy-server-${proxyServerName}`);
const eventCallbackServerPort = fs.readFileSync(tmpFileWithPort, 'utf8');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: Can this be async?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 9090f79

}
eventContents = '';
} else {
eventContents = eventContents.concat(char);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: why concat over push? push is faster, and we already break mutability by using let eventContents

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly string doesn't have a push method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omg im a dummy


const eventCallbackServerStartupPromise = new Promise<void>(resolve => {
const listener = eventCallbackServer.listen(0, () => {
const port = String((listener.address() as AddressInfo).port);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const port = String((listener.address() as AddressInfo).port);
const port = String(eventCallbackServer.address().port);

l: We can then forgo the listener return as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 f2bc116

Co-authored-by: Abhijeet Prasad <aprasad@sentry.io>
@lforst
Copy link
Contributor Author

lforst commented Feb 24, 2023

Do we really need both the eventCallbackServer and a proxyServer?

@AbhiPrasad Thought about just making it different endpoints on the same server but it simplifies defining the tunnel a lot.

@lforst lforst merged commit 21fb51b into develop Feb 24, 2023
@lforst lforst deleted the lforst-event-proxy-server branch February 24, 2023 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add test utility that is able to intercept events
2 participants