Repository navigation
feat(solidstart): Serve the managed tunnel on SolidStart v1 #25423
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
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
53acde0
feat(solidstart): Serve the managed tunnel on SolidStart v1
logaretm 9714075
fix(solidstart): Serve the managed tunnel under `server.baseURL`
logaretm 2492392
test(solidstart): Check the managed tunnel on SolidStart 2
logaretm 55fde50
test(solidstart): Check the managed tunnel under the SolidStart 2 dev…
logaretm 6e7ab1e
test(solidstart): Check the managed tunnel under `vinxi dev` too
logaretm b30d216
fix(solidstart): Normalize the tunnel base path with the core util
logaretm 5e51eb9
fix(solidstart): Read the tunnel request with h3 so serverless preset…
logaretm af77521
fix(solidstart): Serve the tunnel from the first h3 layer so route ru…
logaretm 0fa52ad
ref(solidstart): Report the base path once with setManagedTunnelDefaults
logaretm bd38d8a
test(solidstart): Send Content-Length with the tunnel test requests
logaretm 71d60ae
test(solidstart): Race the tunnel span with the shared helper, and dr…
logaretm 8745a90
ref(solidstart): Let core skip an empty base path, and drop the test …
logaretm ccd94ce
ref: Import the internal tunnel helpers under their `_INTERNAL_` names
logaretm 4ba1bc1
fix(solidstart): Declare h3 as a dependency for the tunnel plugin
logaretm ff0b8c6
test(solidstart): Declare node-mock-http for the tunnel plugin test
logaretm 4a268dd
test(solidstart): Serve the managed tunnel with `serveTunnelIntegration`
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,13 +4,15 @@ import * as Sentry from '@sentry/solidstart'; | |
| import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidrouter'; | ||
| import { StartClient, mount } from '@solidjs/start/client'; | ||
|
|
||
| declare const __E2E_TEST_TUNNEL__: boolean; | ||
|
|
||
| Sentry.init({ | ||
| // We can't use env variables here, seems like they are stripped | ||
| // out in production builds. | ||
| dsn: 'https://[email protected]/1337', | ||
| dsn: __E2E_TEST_TUNNEL__ ? 'http://public@localhost:3031/1337' : 'https://[email protected]/1337', | ||
| environment: 'qa', // dynamic sampling bias to keep transactions | ||
| integrations: [solidRouterBrowserTracingIntegration()], | ||
| tunnel: 'http://localhost:3031/', // proxy server | ||
| tunnel: __E2E_TEST_TUNNEL__ ? true : 'http://localhost:3031/', // proxy server | ||
| // Performance Monitoring | ||
| tracesSampleRate: 1.0, // Capture 100% of the transactions | ||
| debug: !!import.meta.env.DEBUG, | ||
|
|
||
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
25 changes: 25 additions & 0 deletions
25
dev-packages/e2e-tests/test-applications/solidstart/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,25 @@ | ||
| import { expect, test } from '@playwright/test'; | ||
| import { isServerSpanAt, watchForTunnelSpan } from '@sentry-internal/test-utils'; | ||
|
|
||
| test.skip(!process.env.E2E_TEST_TUNNEL, 'Only runs in the tunnel variant'); | ||
|
|
||
| const APP_NAME = 'solidstart'; | ||
| const BASE_PATH = process.env.E2E_TEST_BASE_PATH ?? ''; | ||
| const TUNNEL_PATH = new RegExp(`^${BASE_PATH}/[a-z0-9]{1,7}$`); | ||
|
|
||
| test('answers the managed tunnel before the app middleware', async ({ page }) => { | ||
| // The variant's auth middleware rejects every other POST. | ||
| expect((await page.request.post(`${BASE_PATH}/`, { data: 'x' })).status()).toBe(401); | ||
|
|
||
| const raceTunnelSpan = watchForTunnelSpan(APP_NAME, isServerSpanAt(TUNNEL_PATH)); | ||
| const tunnelResponsePromise = page.waitForResponse( | ||
| response => response.request().method() === 'POST' && TUNNEL_PATH.test(new URL(response.url()).pathname), | ||
| ); | ||
|
|
||
| await page.goto(`${BASE_PATH}/`); | ||
|
|
||
| expect((await tunnelResponsePromise).status()).toBe(200); | ||
| expect( | ||
| await raceTunnelSpan(isServerSpanAt(`${BASE_PATH}/users/6`), () => page.request.get(`${BASE_PATH}/users/6`)), | ||
| ).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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import type { Options } from '@sentry/core'; | ||
| import { _INTERNAL_applyTunnelDefaults as applyTunnelDefaults } from '@sentry/core'; | ||
|
|
||
| /** Set on the server by `withSentry` to the app's `server.baseURL`. */ | ||
| export const TUNNEL_BASE_PATH_GLOBAL = '__sentry_solidstart_base_path'; | ||
|
|
||
| declare const __SENTRY_SOLIDSTART_TUNNEL_BASE_PATH__: string | undefined; | ||
|
|
||
| /** | ||
| * Serves the browser's managed tunnel under the app's `server.baseURL`, which `withSentry` defines at build time. | ||
| * A `basePath` the user set wins. | ||
| */ | ||
| export function applyTunnelBasePath(options: Pick<Options, 'tunnel'>): void { | ||
| const basePath = | ||
| typeof __SENTRY_SOLIDSTART_TUNNEL_BASE_PATH__ !== 'undefined' ? __SENTRY_SOLIDSTART_TUNNEL_BASE_PATH__ : ''; | ||
| options.tunnel = applyTunnelDefaults(options.tunnel, { basePath }); | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import { GLOBAL_OBJ } from '@sentry/core'; | ||
| import { | ||
| handleTunnelRequestIfMatched, | ||
| isTunnelRequest, | ||
| _INTERNAL_setManagedTunnelDefaults as setManagedTunnelDefaults, | ||
| } from '@sentry/core/server'; | ||
| import { eventHandler, toWebRequest } from 'h3'; | ||
| import type { NitroAppPlugin } from 'nitropack'; | ||
| import { TUNNEL_BASE_PATH_GLOBAL } from '../common/tunnel'; | ||
|
|
||
| /** | ||
| * Answers managed tunnel requests on SolidStart v1 (Nitro v2), from the first layer of the h3 app. That runs before | ||
| * route rules, the app's middleware and routing. `withSentry` registers it. | ||
| */ | ||
| export default (nitroApp => { | ||
| // The first layer sees the path before Nitro strips `server.baseURL`, so the tunnel is served under it. The server | ||
| // bundle's banner sets the global before this plugin runs. | ||
| setManagedTunnelDefaults({ | ||
| basePath: (GLOBAL_OBJ as { [TUNNEL_BASE_PATH_GLOBAL]?: string })[TUNNEL_BASE_PATH_GLOBAL], | ||
| }); | ||
|
|
||
| // Not the `request` hook: h3 still runs the first layer after a hook answered, so route rules would act on the | ||
| // sent response. | ||
| nitroApp.h3App.stack.unshift({ | ||
|
logaretm marked this conversation as resolved.
|
||
| route: '', | ||
| handler: eventHandler(event => { | ||
| // Checked on the method and path first, so no other request's body gets wrapped in a web `Request`. | ||
| if (!isTunnelRequest({ method: event.method, url: event.path })) { | ||
| return undefined; | ||
| } | ||
|
|
||
| // `toWebRequest` also reads the body of the mock requests serverless presets pass through `localFetch`, which | ||
| // aren't Node streams. A returned `Response` is sent and ends the request, `undefined` hands it on. | ||
| return handleTunnelRequestIfMatched(toWebRequest(event)); | ||
| }), | ||
| }); | ||
| }) satisfies NitroAppPlugin; | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { afterEach, describe, expect, it, vi } from 'vitest'; | ||
| import { applyTunnelBasePath } from '../../src/common/tunnel'; | ||
|
|
||
| describe('applyTunnelBasePath', () => { | ||
| afterEach(() => { | ||
| vi.unstubAllGlobals(); | ||
| }); | ||
|
|
||
| it('serves the managed tunnel under the defined base path', () => { | ||
| vi.stubGlobal('__SENTRY_SOLIDSTART_TUNNEL_BASE_PATH__', '/app'); | ||
| const options: Parameters<typeof applyTunnelBasePath>[0] = { tunnel: true }; | ||
|
|
||
| applyTunnelBasePath(options); | ||
|
|
||
| expect(options.tunnel).toEqual({ basePath: '/app' }); | ||
| }); | ||
|
|
||
| it('leaves the tunnel alone without a base path', () => { | ||
| const options: Parameters<typeof applyTunnelBasePath>[0] = { tunnel: true }; | ||
|
|
||
| applyTunnelBasePath(options); | ||
|
|
||
| expect(options.tunnel).toBe(true); | ||
| }); | ||
| }); |
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.