Skip to content

Commit 9a40f22

Browse files
committed
do tests work now?
1 parent 66ab3c4 commit 9a40f22

File tree

3 files changed

+146
-3
lines changed

3 files changed

+146
-3
lines changed

packages/aws-serverless/scripts/buildLambdaLayer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ async function buildLambdaLayer(): Promise<void> {
4848
fsForceMkdirSync('./build/aws/dist-serverless/extensions');
4949
fs.copyFileSync('./src/lambda-extension/sentry-extension', './build/aws/dist-serverless/extensions/sentry-extension');
5050
fs.chmodSync('./build/aws/dist-serverless/extensions/sentry-extension', 0o755);
51+
fs.chmodSync('./build/aws/dist-serverless/sentry-extension/index.js', 0o755);
5152

5253
const zipFilename = `sentry-node-serverless-${version}.zip`;
5354
console.log(`Creating final layer zip file ${zipFilename}.`);

packages/aws-serverless/src/lambda-extension/aws-lambda-extension.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as http from 'node:http';
22
import { buffer } from 'node:stream/consumers';
3+
import { dsnStringToIngestEndpoint } from './utils';
34

45
/**
56
* The Extension API Client.
@@ -109,9 +110,7 @@ export class AwsLambdaExtension {
109110
const envelope = new TextDecoder().decode(envelopeBytes);
110111
const piece = envelope.split('\n')[0];
111112
const header = JSON.parse(piece ?? '{}') as { dsn?: string };
112-
const dsn = new URL(header.dsn || '');
113-
const projectId = dsn.pathname.replace('/', '');
114-
const upstreamSentryUrl = `https://${dsn.hostname}/api/${projectId}/envelope/`;
113+
const upstreamSentryUrl = dsnStringToIngestEndpoint(header.dsn || '');
115114

116115
console.log('tunneling to sentry', { upstreamSentryUrl });
117116
console.log('headers', req.headers);
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/** Supported Sentry transport protocols in a Dsn. */
2+
export type DsnProtocol = 'http' | 'https';
3+
4+
/** Primitive components of a Dsn. */
5+
interface DsnComponents {
6+
/** Protocol used to connect to Sentry. */
7+
protocol: DsnProtocol;
8+
/** Public authorization key. */
9+
publicKey?: string;
10+
/** Private authorization key (deprecated, optional). */
11+
pass?: string;
12+
/** Hostname of the Sentry instance. */
13+
host: string;
14+
/** Port of the Sentry instance. */
15+
port?: string;
16+
/** Sub path/ */
17+
path?: string;
18+
/** Project ID */
19+
projectId: string;
20+
}
21+
22+
interface Package {
23+
name: string;
24+
version: string;
25+
dependencies?: Record<string, string>;
26+
devDependencies?: Record<string, string>;
27+
}
28+
29+
interface SdkInfo {
30+
name?: string;
31+
version?: string;
32+
integrations?: string[];
33+
packages?: Package[];
34+
settings?: {
35+
infer_ip?: 'auto' | 'never';
36+
};
37+
}
38+
39+
const SENTRY_API_VERSION = '7';
40+
41+
/**
42+
* Converts a Dsn string to the ingest endpoint URL.
43+
*
44+
* @param dsnString A Dsn as string
45+
* @returns The ingest endpoint URL
46+
*/
47+
export function dsnStringToIngestEndpoint(dsnString: string): string {
48+
const dsn = dsnFromString(dsnString);
49+
if (!dsn) {
50+
throw new Error(`Invalid Sentry Dsn: ${dsnString}`);
51+
}
52+
return getEnvelopeEndpointWithUrlEncodedAuth(dsn);
53+
}
54+
55+
/** Returns the prefix to construct Sentry ingestion API endpoints. */
56+
function getBaseApiEndpoint(dsn: DsnComponents): string {
57+
const protocol = dsn.protocol ? `${dsn.protocol}:` : '';
58+
const port = dsn.port ? `:${dsn.port}` : '';
59+
return `${protocol}//${dsn.host}${port}${dsn.path ? `/${dsn.path}` : ''}/api/`;
60+
}
61+
62+
/** Returns the ingest API endpoint for target. */
63+
function _getIngestEndpoint(dsn: DsnComponents): string {
64+
return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/envelope/`;
65+
}
66+
67+
/** Returns a URL-encoded string with auth config suitable for a query string. */
68+
function _encodedAuth(dsn: DsnComponents, sdkInfo: SdkInfo | undefined): string {
69+
const params: Record<string, string> = {
70+
sentry_version: SENTRY_API_VERSION,
71+
};
72+
73+
if (dsn.publicKey) {
74+
// We send only the minimum set of required information. See
75+
// https://github.com/getsentry/sentry-javascript/issues/2572.
76+
params.sentry_key = dsn.publicKey;
77+
}
78+
79+
if (sdkInfo) {
80+
params.sentry_client = `${sdkInfo.name}/${sdkInfo.version}`;
81+
}
82+
83+
return new URLSearchParams(params).toString();
84+
}
85+
86+
/**
87+
* Returns the envelope endpoint URL with auth in the query string.
88+
*
89+
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
90+
*/
91+
function getEnvelopeEndpointWithUrlEncodedAuth(dsn: DsnComponents, tunnel?: string, sdkInfo?: SdkInfo): string {
92+
return tunnel ? tunnel : `${_getIngestEndpoint(dsn)}?${_encodedAuth(dsn, sdkInfo)}`;
93+
}
94+
95+
const DSN_REGEX = /^(?:(\w+):)\/\/(?:(\w+)(?::(\w+)?)?@)([\w.-]+)(?::(\d+))?\/(.+)/;
96+
97+
/**
98+
* Parses a Dsn from a given string.
99+
*
100+
* @param str A Dsn as string
101+
* @returns Dsn as DsnComponents or undefined if @param str is not a valid DSN string
102+
*/
103+
function dsnFromString(str: string): DsnComponents | undefined {
104+
const match = DSN_REGEX.exec(str);
105+
106+
if (!match) {
107+
// eslint-disable-next-line no-console
108+
console.error(`Invalid Sentry Dsn: ${str}`);
109+
110+
return undefined;
111+
}
112+
113+
const [protocol, publicKey, pass = '', host = '', port = '', lastPath = ''] = match.slice(1);
114+
let path = '';
115+
let projectId = lastPath;
116+
117+
const split = projectId.split('/');
118+
if (split.length > 1) {
119+
path = split.slice(0, -1).join('/');
120+
projectId = split.pop() as string;
121+
}
122+
123+
if (projectId) {
124+
const projectMatch = projectId.match(/^\d+/);
125+
if (projectMatch) {
126+
projectId = projectMatch[0];
127+
}
128+
}
129+
130+
return dsnFromComponents({ host, pass, path, projectId, port, protocol: protocol as DsnProtocol, publicKey });
131+
}
132+
133+
function dsnFromComponents(components: DsnComponents): DsnComponents {
134+
return {
135+
protocol: components.protocol,
136+
publicKey: components.publicKey || '',
137+
pass: components.pass || '',
138+
host: components.host,
139+
port: components.port || '',
140+
path: components.path || '',
141+
projectId: components.projectId,
142+
};
143+
}

0 commit comments

Comments
 (0)