Repository navigation
feat(nextjs): Serve the tunnel from the app with a DSN-derived path #25424
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
Draft
+5,222
−716
Draft
Changes from all commits
Commits
Show all changes
93 commits
Select commit
Hold shift + click to select a range
ed011c1
feat(core): Add managed tunnel helpers that derive the tunnel path fr…
logaretm 1ed77e7
ref(core): Pass the whole request to the tunnel matcher and expose `g…
logaretm 9f81b8d
perf(core): Cache the tunnel path per DSN
logaretm 449f3f5
feat(core): Serve a fixed managed tunnel path from a relative `tunnel…
logaretm be49ea1
feat(core): Drop the server spans of managed tunnel requests
logaretm 2b8e376
fix(core): Skip fetch instrumentation while tracing is suppressed
logaretm 2b03c6a
feat(core): Check the tunnel DSN before reading the envelope body
logaretm 4fcb0cb
fix(core): Cancel the body of a rejected tunnel request
logaretm 2af4e5e
feat(core): Forward the client IP of tunnel requests to Sentry
logaretm fda805e
fix(core): Match a fixed tunnel path that ends with a slash
logaretm d348c1b
fix(core): Only answer the managed tunnel under its base path
logaretm 69719b6
feat(core): Always accept the server's own DSN in the managed tunnel
logaretm 95d228f
feat(core): Add `applyTunnelDefaults` so framework SDKs fill in the b…
logaretm f35b3a3
feat(core): Add `setManagedTunnelBasePath` for servers that learn the…
logaretm b9d60f8
feat(core): Mark the tunnel handler's responses so the browser can te…
logaretm 0ad10d4
fix(core): Normalize tunnel base paths with one util that doesn't bac…
logaretm ee9dcd5
fix(core): Leave a fixed tunnel path to the app when there is no DSN …
logaretm 5653798
fix(core): Don't add the base path again to a tunnel path that alread…
logaretm 4a999a3
fix(core): Don't report a client that drops the tunnel request as an …
logaretm 3017e68
fix(core): Also drop tunnel spans that only carry `http.url`, like Sv…
logaretm c7a419a
feat(core): Add enableManagedTunnel for framework options that enable…
logaretm c54586d
ref(core): Settle a server client's managed tunnel in one place, from…
logaretm 5a0ddf8
feat: Accept `tunnel: true` in `init` to enable the managed tunnel
logaretm bf1178f
feat: Explain a managed tunnel that nothing on the server answers
logaretm 9fcc375
fix(core): Keep the managed tunnel warning out of production bundles
logaretm 1237306
fix(aws-serverless): Keep the managed tunnel when the Lambda extensio…
logaretm b904659
fix(core): Point Next.js users to `tunnelRoute` when the managed tunn…
logaretm e1905f7
feat(browser): Send directly to Sentry when nothing answers the manag…
logaretm 2f84ea9
fix(browser): Normalize the managed tunnel's base path like the serve…
logaretm 87198eb
fix(browser): Keep the managed tunnel when Sentry answers 404 or 405 …
logaretm 4238303
fix(browser): Send directly when anything but the tunnel answers a ma…
logaretm 04cf6f0
ref(browser): Warn about a rejected DSN from the managed tunnel trans…
logaretm 328fa66
feat(tanstackstart-react): Serve the managed tunnel from the server e…
logaretm d89155b
feat(tanstackstart-react): Serve the managed tunnel under the base pa…
logaretm 1b4a9c5
fix(tanstackstart-react): Drop the tunnel's spans from the Vite dev s…
logaretm 0414802
fix(tanstackstart-react): Normalize the tunnel base path with the cor…
logaretm 4d9789c
fix(tanstackstart-react): Pass a Worker's env and ctx through the tun…
logaretm fcdb6e6
fix(tanstackstart-react): Fail the build when a fixed tunnelRoute col…
logaretm ab02b21
docs(tanstackstart-react): Describe how tunnelRoute is served now
logaretm a64d809
ref(tanstackstart-react): Enable tunnelRoute on the client like init …
logaretm 76dfbbb
ref(tanstackstart-react): Report tunnelRoute and the base path once w…
logaretm 2689a18
test: Check that runtimes drop the server spans of managed tunnel req…
logaretm 934f55e
test(bun): Run the managed tunnel suite only with the Bun SDK
logaretm 2fbbe93
test(node): Assert the tunnel request sends no server span at all
logaretm 366bdb7
feat(sveltekit): Serve the managed tunnel from `sentryHandle` under t…
logaretm 1d45cb7
fix(sveltekit): Keep source maps of the hooks files under a base path
logaretm ab3ce59
fix(sveltekit): Serve a relative `tunnel` path under the base path too
logaretm 3699521
test(sveltekit): Check the managed tunnel under `vite dev` too
logaretm 0b4cba0
test(sveltekit): Check no span of a tunnel request is sent with Kit t…
logaretm 7e96182
ref(sveltekit): Import the tunnel handler from @sentry/core/server
logaretm 7496f1d
feat(nuxt, nitro): Serve the managed tunnel before the app's server m…
logaretm 588d663
feat(nuxt, nitro): Serve the managed tunnel under the app's base URL
logaretm 694b885
fix(nitro): Drop the tunnel's dev server spans under `baseURL` too
logaretm ea8b717
fix(nuxt): Normalize the tunnel base path with the core util
logaretm 1cc04f9
fix(nuxt, nitro): Serve the managed tunnel from `nitroApp.fetch` so r…
logaretm bd02881
fix(nuxt): Serve the Nitro v2 tunnel from the first h3 layer so route…
logaretm 62d62fd
test(nitro): Check a client dropping the tunnel request isn't reporte…
logaretm b18e464
fix(nuxt): Serve the managed tunnel when the SDK is set up with sentr…
logaretm fb58f66
ref(nuxt, nitro): Report the base URL once with setManagedTunnelDefau…
logaretm 34649a5
feat(astro): Serve the managed tunnel from the Sentry middleware
logaretm b4453e3
fix(astro): Run the server init before the middleware so the tunnel w…
logaretm a1c95e4
fix(astro): Serve a relative `tunnel` path under the base path too
logaretm a0a4102
test(astro): Check the managed tunnel under `astro dev` too
logaretm 4d98700
revert(astro): Load the server init from SSR pages only, as on develop
logaretm bbc97ee
ref(astro): Import the tunnel handler from @sentry/core/server
logaretm a45041b
feat(remix): Serve the managed tunnel from the Remix 3 router middleware
logaretm c2a06f6
ref(remix): Import the tunnel handler from @sentry/core/server
logaretm 6069ffe
feat(react-router): Serve the managed tunnel from a route in the serv…
logaretm 13d36b4
test(react-router): Define the tunnel flag in the Cloudflare build
logaretm eb1d918
fix(react-router): Serve the managed tunnel under the app's `basename`
logaretm 9affda3
test(react-router): Check the managed tunnel under `react-router dev`…
logaretm f90ba56
fix(react-router): Normalize the tunnel base path with the core util
logaretm 068c11f
fix(react-router): Serve every managed tunnel path, one route each
logaretm 1ccbbae
ref(react-router): Report the basename once with setManagedTunnelDefa…
logaretm 3591362
feat(solidstart): Serve the managed tunnel on SolidStart v1
logaretm 3ef6ec4
fix(solidstart): Serve the managed tunnel under `server.baseURL`
logaretm d4c1fa8
test(solidstart): Check the managed tunnel on SolidStart 2
logaretm 60f8a5e
test(solidstart): Check the managed tunnel under the SolidStart 2 dev…
logaretm 9ca3f64
test(solidstart): Check the managed tunnel under `vinxi dev` too
logaretm c101e43
fix(solidstart): Normalize the tunnel base path with the core util
logaretm deee419
fix(solidstart): Read the tunnel request with h3 so serverless preset…
logaretm 89f6ef6
fix(solidstart): Serve the tunnel from the first h3 layer so route ru…
logaretm d6f8d7d
ref(solidstart): Report the base path once with setManagedTunnelDefaults
logaretm 65160f0
feat(nextjs): Serve the tunnel from the app with a DSN-derived path
logaretm e0d1111
fix(nextjs): Read the injected tunnel options when the client has none
logaretm 9ad5e7d
test(nextjs): Wait for a later span instead of a timeout in the tunne…
logaretm 13bb279
fix(nextjs): Write the tunnel route into the root app dir when the ro…
logaretm 43c4a5d
test(nextjs): Wait for a later span instead of a timeout in the pages…
logaretm bf77f91
fix(nextjs): Point middleware at the tunnel path instead of the envel…
logaretm 1efc017
feat(nextjs): Accept allowed DSNs in `tunnelRoute` for a browser DSN …
logaretm 8cc5098
feat(nextjs): Export `isTunnelRequest` so middleware can let the tunn…
logaretm b3177a2
fix(nextjs): Serve the server's own DSN path alongside `allowedDsns`
logaretm 5bc562c
ref(nextjs): Import the tunnel helpers from @sentry/core/server
logaretm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
dev-packages/e2e-tests/test-applications/astro-7/src/middleware.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { defineMiddleware } from 'astro:middleware'; | ||
|
|
||
| // The tunnel variant checks that the managed tunnel answers before an auth middleware like this one. | ||
| export const onRequest = defineMiddleware((context, next) => { | ||
| if ( | ||
| import.meta.env.PUBLIC_E2E_TEST_TUNNEL && | ||
| context.request.method === 'POST' && | ||
| context.request.headers.get('x-auth') !== 'ok' | ||
| ) { | ||
| return new Response('blocked by user auth', { status: 401 }); | ||
| } | ||
|
|
||
| return next(); | ||
| }); |
43 changes: 43 additions & 0 deletions
43
dev-packages/e2e-tests/test-applications/astro-7/tests/tunnel.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { expect, test } from '@playwright/test'; | ||
| import { getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils'; | ||
|
|
||
| test.skip(!process.env.PUBLIC_E2E_TEST_TUNNEL, 'Only runs in the tunnel variant'); | ||
|
|
||
| const TUNNEL_PATH = /^\/docs\/[a-z0-9]{1,7}\/$/; | ||
|
|
||
| test('answers the managed tunnel under the base path before the app middleware', async ({ page }) => { | ||
| // The variant's auth middleware rejects every other POST. | ||
| expect((await page.request.post('/docs/anything/', { data: 'x' })).status()).toBe(401); | ||
|
|
||
| const tunnelServerSpanPromise = waitForStreamedSpan( | ||
| 'astro-7', | ||
| span => | ||
| getSpanOp(span) === 'http.server' && | ||
| /\/docs\/[a-z0-9]{1,7}\/?$/.test(String(span.attributes['url.path']?.value ?? '')), | ||
| ); | ||
| const tunnelResponsePromise = page.waitForResponse( | ||
| response => response.request().method() === 'POST' && TUNNEL_PATH.test(new URL(response.url()).pathname), | ||
| ); | ||
|
|
||
| await page.goto('/docs/'); | ||
| const tunnelResponse = await tunnelResponsePromise; | ||
|
|
||
| expect(tunnelResponse.status()).toBe(200); | ||
| expect(new URL(tunnelResponse.url()).origin).toBe(new URL(page.url()).origin); | ||
|
|
||
| // Anchor on a regular server span requested after the tunnel traffic: spans flush in order, so a tunnel span | ||
| // would arrive first. | ||
| const anchorSpanPromise = waitForStreamedSpan( | ||
| 'astro-7', | ||
| span => | ||
| getSpanOp(span) === 'http.server' && | ||
| String(span.attributes['url.path']?.value ?? '').startsWith('/docs/test-ssr'), | ||
| ); | ||
| await page.request.get('/docs/test-ssr/'); | ||
|
|
||
| const winner = await Promise.race([ | ||
| tunnelServerSpanPromise.then(() => 'tunnel' as const), | ||
| anchorSpanPromise.then(() => 'anchor' as const), | ||
| ]); | ||
| expect(winner).toBe('anchor'); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 7 additions & 1 deletion
8
dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/proxy.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
dev-packages/e2e-tests/test-applications/nextjs-16-tunnel/tests/tunnel-dsn-path.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import { expect, test } from '@playwright/test'; | ||
| import { getSpanOp, waitForError, waitForStreamedSpan, waitForStreamedSpans } from '@sentry-internal/test-utils'; | ||
| import type { SerializedStreamedSpan } from '@sentry/core'; | ||
| import { getTunnelPath } from '@sentry/core'; | ||
|
|
||
| const TUNNEL_PATH = getTunnelPath('http://public@localhost:3031/1337'); | ||
|
|
||
| test('sends events to the DSN-derived path, served by the app', async ({ page }) => { | ||
| const tunnelRequests: string[] = []; | ||
| page.on('request', request => { | ||
| if (request.method() === 'POST') { | ||
| tunnelRequests.push(new URL(request.url()).pathname); | ||
| } | ||
| }); | ||
|
|
||
| const errorPromise = waitForError('nextjs-16-tunnel', event => { | ||
| return event.exception?.values?.[0]?.value === 'Managed tunnel error'; | ||
| }); | ||
|
|
||
| await page.goto('/'); | ||
| await page.evaluate(() => { | ||
| setTimeout(() => { | ||
| throw new Error('Managed tunnel error'); | ||
| }); | ||
| }); | ||
|
|
||
| expect(await errorPromise).toBeDefined(); | ||
| expect(tunnelRequests).toContain(TUNNEL_PATH); | ||
| }); | ||
|
|
||
| test('does not create server spans for tunnel requests', async ({ page }) => { | ||
| const serverSpans: SerializedStreamedSpan[] = []; | ||
| void waitForStreamedSpans('nextjs-16-tunnel', spans => { | ||
| serverSpans.push(...spans.filter(span => span.is_segment)); | ||
| return false; | ||
| }); | ||
|
|
||
| const errorPromise = waitForError('nextjs-16-tunnel', event => { | ||
| return event.exception?.values?.[0]?.value === 'Managed tunnel span error'; | ||
| }); | ||
|
|
||
| await page.goto('/'); | ||
| await page.evaluate(() => { | ||
| setTimeout(() => { | ||
| throw new Error('Managed tunnel span error'); | ||
| }); | ||
| }); | ||
| await errorPromise; | ||
|
|
||
| // Spans flush in order, so a span for the tunnel request would arrive before the one for this request. | ||
| const anchorSpanPromise = waitForStreamedSpan( | ||
| 'nextjs-16-tunnel', | ||
| span => span.is_segment && getSpanOp(span) === 'http.server' && span.name === 'GET /', | ||
| ); | ||
| await page.request.get('/'); | ||
| await anchorSpanPromise; | ||
|
|
||
| const tunnelSpans = serverSpans.filter( | ||
| span => span.name.includes(TUNNEL_PATH!) || span.name.includes('sentry-tunnel'), | ||
| ); | ||
| expect(tunnelSpans).toEqual([]); | ||
|
logaretm marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| test('lets tunnel requests through auth middleware with `isTunnelRequest`', async ({ request }) => { | ||
| const headers = { 'content-type': 'application/x-sentry-envelope', 'x-e2e-require-auth': '1' }; | ||
| const data = `${JSON.stringify({ dsn: 'http://public@localhost:3031/1337' })}\n`; | ||
|
|
||
| expect((await request.post(TUNNEL_PATH!, { headers, data })).status()).toBe(200); | ||
| expect((await request.post('/notatunnel', { headers, data })).status()).toBe(401); | ||
| }); | ||
|
|
||
| test('answers 404 to an envelope POST on any other path', async ({ request }) => { | ||
| const response = await request.post('/notatunnel', { | ||
| headers: { 'content-type': 'application/x-sentry-envelope' }, | ||
| data: `${JSON.stringify({ dsn: 'http://public@localhost:3031/1337' })}\n`, | ||
| }); | ||
|
|
||
| expect(response.status()).toBe(404); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.