Skip to content

Commit ab19826

Browse files
committed
Merge remote-tracking branch 'upstream/master' into node/refactor-stack-parsing
2 parents ba2400d + 8bcd867 commit ab19826

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

packages/nextjs/test/integration/test/server/tracingHttp.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ module.exports = async ({ url: urlBase, argv }) => {
4040
'tracingHttp',
4141
);
4242

43-
await getAsync(url);
43+
// The `true` causes `getAsync` to rewrap `http.get` in next 12, since it will have been overwritten by the import of
44+
// `nock` above. See https://github.com/getsentry/sentry-javascript/pull/4619.
45+
// TODO: see note in `getAsync` about removing the boolean
46+
await getAsync(url, true);
4447
await sleep(250);
4548

4649
assert.ok(capturedRequest.isDone(), 'Did not intercept expected request');

packages/nextjs/test/integration/test/utils/server.js

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
const { get } = require('http');
22
const nock = require('nock');
3+
const nodeSDK = require('@sentry/node');
34
const { logIf, parseEnvelope } = require('./common');
45

56
Error.stackTraceLimit = Infinity;
67

7-
const getAsync = url => {
8+
const getAsync = (url, rewrap = false) => {
9+
// Depending on what version of Nextjs we're testing, the wrapping which happens in the `Http` integration may have
10+
// happened too early and gotten overwritten by `nock`. This redoes the wrapping if so.
11+
//
12+
// TODO: This works but is pretty hacky in that it has the potential to wrap things multiple times, more even than the
13+
// double-wrapping which is discussed at length in the comment in `ensureWrappedGet` below, which is why we need
14+
// `rewrap`. Once we fix `fill` to not wrap things twice, we should be able to take this out and just always call
15+
// `ensureWrappedGet`.
16+
const wrappedGet = rewrap ? ensureWrappedGet(get, url) : get;
17+
818
return new Promise((resolve, reject) => {
9-
get(url, res => {
19+
wrappedGet(url, res => {
1020
res.setEncoding('utf8');
1121
let rawData = '';
1222
res.on('data', chunk => {
@@ -99,6 +109,60 @@ const objectMatches = (actual, expected) => {
99109
return true;
100110
};
101111

112+
/**
113+
* Rewrap `http.get` if the wrapped version has been overridden by `nock`.
114+
*
115+
* This is only relevant for Nextjs >= 12.1, which changed when `_app` is initialized, which in turn changed the order
116+
* in which our SDK and `nock` wrap `http.get`. See https://github.com/getsentry/sentry-javascript/pull/4619.
117+
*
118+
* TODO: We'll have to do this for `ClientRequest` also if we decide to start wrapping that.
119+
* TODO: Can we fix the wrapping-things-twice problem discussed in the comment below?
120+
*/
121+
function ensureWrappedGet(importedGet, url) {
122+
// we always test against the latest minor for any given Nextjs major version, so if we're testing Next 12, it's
123+
// definitely at least 12.1, making this check against the major version sufficient
124+
if (Number(process.env.NEXTJS_VERSION) < 12) {
125+
return importedGet;
126+
}
127+
128+
// As of Next 12.1, creating a `NextServer` instance (which we do immediately upon starting this test runner) loads
129+
// `_app`, which has the effect of initializing the SDK. So, unless something's gone wrong, we should always be able
130+
// to find the integration
131+
let httpIntegration;
132+
try {
133+
httpIntegration = nodeSDK.getCurrentHub().getClient().getIntegration(nodeSDK.Integrations.Http);
134+
} catch (err) {
135+
console.warn(`Warning: Sentry SDK not set up at \`NextServer\` initialization. Request URL: ${url}`);
136+
return importedGet;
137+
}
138+
139+
// This rewraps `http.get` and `http.request`, which, at this point, look like `nockWrapper(sentryWrapper(get))` and
140+
// `nockWrapper(sentryWrapper(request))`. By the time we're done with this function, they'll look like
141+
// `sentryWrapper(nockWrapper(sentryWrapper(get)))` and `sentryWrapper(nockWrapper(sentryWrapper(request)))`,
142+
// respectively. Though this seems less than ideal, we don't have to worry about our instrumentation being
143+
// (meaningfully) called twice because:
144+
//
145+
// 1) As long as we set up a `nock` interceptor for any outgoing http request, `nock`'s wrapper will call a replacement
146+
// function for that request rather than call the function it's wrapping (in other words, it will look more like
147+
// `sentryWrapper(nockWrapper(getSubstitute))` than `sentryWrapper(nockWrapper(sentryWrapper(get)))`), which means
148+
// our code is only called once.
149+
// 2) In cases where we don't set up an interceptor (such as for the `wrappedGet` call in `getAsync` above), it's true
150+
// that we can end up with `sentryWrapper(nockWrapper(sentryWrapper(get)))`, meaning our wrapper code will run
151+
// twice. For now that's okay because in those cases we're not in the middle of a transactoin and therefore
152+
// the two wrappers' respective attempts to start spans will both no-op.
153+
//
154+
// TL; DR - if the double-wrapping means you're seeing two spans where you really only want one, set up a nock
155+
// interceptor for the request.
156+
//
157+
// TODO: add in a "don't do this twice" check (in `fill`, maybe moved from `wrap`), so that we don't wrap the outer
158+
// wrapper with a third wrapper
159+
httpIntegration.setupOnce();
160+
161+
// now that we've rewrapped it, grab the correct version of `get` for use in our tests
162+
const httpModule = require('http');
163+
return httpModule.get;
164+
}
165+
102166
module.exports = {
103167
getAsync,
104168
interceptEventRequest,

packages/nextjs/test/run-integration-tests.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ mv next.config.js next.config.js.bak
3232

3333
for NEXTJS_VERSION in 10 11 12; do
3434

35+
# export this to the env so that we can behave differently depending on which version of next we're testing, without
36+
# having to pass this value from function to function to function to the one spot, deep in some callstack, where we
37+
# actually need it
38+
export NEXTJS_VERSION=$NEXTJS_VERSION
39+
3540
# Next 10 requires at least Node v10
3641
if [ "$NODE_MAJOR" -lt "10" ]; then
3742
echo "[nextjs] Next.js is not compatible with versions of Node older than v10. Current version $NODE_VERSION"

0 commit comments

Comments
 (0)