Skip to content
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

feat(core): Add tunnel server helper #14137

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

timfish
Copy link
Collaborator

@timfish timfish commented Oct 30, 2024

Closes #13453 and gives a simpler path for #10309 and adding tunnel support to other frameworks.

The current tunnel troubleshooting docs suggest this code but it doesn't work with binary envelopes so breaks with compressed replay envelopes and attachments.

const SENTRY_HOST = "oXXXXXX.ingest.sentry.io";
const SENTRY_PROJECT_IDS = ["123456"];

export const postAction = async ({ request }) => {
  try {
    const envelopeBytes = await req.arrayBuffer();
    const envelope = new TextDecoder().decode(envelopeBytes);
    const piece = envelope.split("\n")[0];
    const header = JSON.parse(piece);
    const dsn = new URL(header["dsn"]);
    const project_id = dsn.pathname?.replace("/", "");

    if (dsn.hostname !== SENTRY_HOST) {
      throw new Error(`Invalid sentry hostname: ${dsn.hostname}`);
    }

    if (!project_id || !SENTRY_PROJECT_IDS.includes(project_id)) {
      throw new Error(`Invalid sentry project id: ${project_id}`);
    }

    const upstream_sentry_url = `https://${SENTRY_HOST}/api/${project_id}/envelope/`;
    await fetch(upstream_sentry_url, {
      method: "POST",
      body: envelopeBytes,
    });

    return json({}, { status: 200 });
  } catch (e) {
    console.error("error tunneling to sentry", e);
    return json({ error: "error tunneling to sentry" }, { status: 500 });
  }
};

Rather than get users to parse their own envelopes for this info, this PR adds a handleTunnelEnvelope function to @sentry/core which handles all the logic for forwarding envelopes.

If your client DSN is the same as your server DSN, the above example code becomes:

export const postAction = async ({ request }) => {
  try {
    await handleTunnelEnvelope(await request.arrayBuffer());
    return json({}, { status: 200 });
  } catch (e) {
    console.error("error tunneling to sentry", e);
    return json({ error: "error tunneling to sentry" }, { status: 500 });
  }
};

If you want to forward envelopes for other DSNs, you can just pass an array as dsnAllowList in the options. Not sure about this naming. Is allowedDsns better?

We can further wrap the handleTunnelEnvelope function and expose more complete examples for each framework. For example we could expose a helper which takes a Request and returns a Response or export an API route ready to use.

@timfish timfish self-assigned this Oct 30, 2024
@timfish timfish marked this pull request as ready for review October 31, 2024 00:44
@mydea
Copy link
Member

mydea commented Oct 31, 2024

I like it! IMHO it is not that easy to handle envelopes (since replay), so it's nice if we can provide this to users instead of asking everybody to re-implement this.

@timfish
Copy link
Collaborator Author

timfish commented Oct 31, 2024

The envelope parsing utils are also all in @sentry/utils which isn't part of our public API and could technically change between minor versions.

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.

Replay tunnel fails due to empty envelope
2 participants