Skip to content

Commit a301579

Browse files
committed
Merge branch 'develop' of https://github.com/getsentry/sentry-javascript into feature/parameterize-public-api
2 parents e23ac38 + 29d597f commit a301579

File tree

6 files changed

+201
-63
lines changed

6 files changed

+201
-63
lines changed

dev-packages/node-integration-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"mysql": "^2.18.1",
4545
"nock": "^13.1.0",
4646
"pg": "^8.7.3",
47+
"proxy": "^2.1.1",
4748
"yargs": "^16.2.0"
4849
},
4950
"config": {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const http = require('http');
2+
const Sentry = require('@sentry/node');
3+
const { createProxy } = require('proxy');
4+
5+
const proxy = createProxy(http.createServer());
6+
proxy.listen(0, () => {
7+
const proxyPort = proxy.address().port;
8+
9+
Sentry.init({
10+
dsn: process.env.SENTRY_DSN,
11+
debug: true,
12+
transportOptions: {
13+
proxy: `http://localhost:${proxyPort}`,
14+
},
15+
});
16+
17+
Sentry.captureMessage('Hello, via proxy!');
18+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createRunner } from '../../utils/runner';
2+
3+
test('proxies sentry requests', done => {
4+
createRunner(__dirname, 'basic.js')
5+
.withMockSentryServer()
6+
.ignore('session')
7+
.expect({
8+
event: {
9+
message: 'Hello, via proxy!',
10+
},
11+
})
12+
.start(done);
13+
});

dev-packages/node-integration-tests/utils/runner.ts

Lines changed: 96 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { spawn } from 'child_process';
33
import { join } from 'path';
44
import type { Envelope, EnvelopeItemType, Event, SerializedSession } from '@sentry/types';
55
import axios from 'axios';
6+
import { createBasicSentryServer } from './server';
67

78
export function assertSentryEvent(actual: Event, expected: Event): void {
89
expect(actual).toMatchObject({
@@ -37,6 +38,18 @@ export function cleanupChildProcesses(): void {
3738
}
3839
}
3940

41+
/** Promise only resolves when fn returns true */
42+
async function waitFor(fn: () => boolean, timeout = 10_000): Promise<void> {
43+
let remaining = timeout;
44+
while (fn() === false) {
45+
await new Promise<void>(resolve => setTimeout(resolve, 100));
46+
remaining -= 100;
47+
if (remaining < 0) {
48+
throw new Error('Timed out waiting for server port');
49+
}
50+
}
51+
}
52+
4053
type Expected =
4154
| {
4255
event: Partial<Event> | ((event: Event) => void);
@@ -48,15 +61,15 @@ type Expected =
4861
session: Partial<SerializedSession> | ((event: SerializedSession) => void);
4962
};
5063

51-
/** */
64+
/** Creates a test runner */
5265
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
5366
export function createRunner(...paths: string[]) {
5467
const testPath = join(...paths);
5568

5669
const expectedEnvelopes: Expected[] = [];
5770
const flags: string[] = [];
5871
const ignored: EnvelopeItemType[] = [];
59-
let hasExited = false;
72+
let withSentryServer = false;
6073

6174
if (testPath.endsWith('.ts')) {
6275
flags.push('-r', 'ts-node/register');
@@ -71,71 +84,36 @@ export function createRunner(...paths: string[]) {
7184
flags.push(...args);
7285
return this;
7386
},
87+
withMockSentryServer: function () {
88+
withSentryServer = true;
89+
return this;
90+
},
7491
ignore: function (...types: EnvelopeItemType[]) {
7592
ignored.push(...types);
7693
return this;
7794
},
7895
start: function (done?: (e?: unknown) => void) {
7996
const expectedEnvelopeCount = expectedEnvelopes.length;
80-
let envelopeCount = 0;
81-
let serverPort: number | undefined;
82-
83-
const child = spawn('node', [...flags, testPath]);
8497

85-
CHILD_PROCESSES.add(child);
86-
87-
child.on('close', () => {
88-
hasExited = true;
89-
});
90-
91-
// Pass error to done to end the test quickly
92-
child.on('error', e => {
93-
done?.(e);
94-
});
98+
let envelopeCount = 0;
99+
let scenarioServerPort: number | undefined;
100+
let hasExited = false;
101+
let child: ReturnType<typeof spawn> | undefined;
95102

96-
async function waitForServerPort(timeout = 10_000): Promise<void> {
97-
let remaining = timeout;
98-
while (serverPort === undefined) {
99-
await new Promise<void>(resolve => setTimeout(resolve, 100));
100-
remaining -= 100;
101-
if (remaining < 0) {
102-
throw new Error('Timed out waiting for server port');
103-
}
104-
}
103+
function complete(error?: Error): void {
104+
child?.kill();
105+
done?.(error);
105106
}
106107

107108
/** Called after each expect callback to check if we're complete */
108109
function expectCallbackCalled(): void {
109110
envelopeCount++;
110111
if (envelopeCount === expectedEnvelopeCount) {
111-
child.kill();
112-
done?.();
112+
complete();
113113
}
114114
}
115115

116-
function tryParseLine(line: string): void {
117-
// Lines can have leading '[something] [{' which we need to remove
118-
const cleanedLine = line.replace(/^.*?] \[{"/, '[{"');
119-
120-
// See if we have a port message
121-
if (cleanedLine.startsWith('{"port":')) {
122-
const { port } = JSON.parse(cleanedLine) as { port: number };
123-
serverPort = port;
124-
return;
125-
}
126-
127-
// Skip any lines that don't start with envelope JSON
128-
if (!cleanedLine.startsWith('[{')) {
129-
return;
130-
}
131-
132-
let envelope: Envelope | undefined;
133-
try {
134-
envelope = JSON.parse(cleanedLine) as Envelope;
135-
} catch (_) {
136-
return;
137-
}
138-
116+
function newEnvelope(envelope: Envelope): void {
139117
for (const item of envelope[1]) {
140118
const envelopeItemType = item[0].type;
141119

@@ -190,22 +168,77 @@ export function createRunner(...paths: string[]) {
190168
expectCallbackCalled();
191169
}
192170
} catch (e) {
193-
done?.(e);
171+
complete(e as Error);
194172
}
195173
}
196174
}
197175

198-
let buffer = Buffer.alloc(0);
199-
child.stdout.on('data', (data: Buffer) => {
200-
// This is horribly memory inefficient but it's only for tests
201-
buffer = Buffer.concat([buffer, data]);
176+
const serverStartup: Promise<number | undefined> = withSentryServer
177+
? createBasicSentryServer(newEnvelope)
178+
: Promise.resolve(undefined);
179+
180+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
181+
serverStartup.then(mockServerPort => {
182+
const env = mockServerPort
183+
? { ...process.env, SENTRY_DSN: `http://public@localhost:${mockServerPort}/1337` }
184+
: process.env;
185+
186+
// eslint-disable-next-line no-console
187+
if (process.env.DEBUG) console.log('starting scenario', testPath, flags, env.SENTRY_DSN);
188+
189+
child = spawn('node', [...flags, testPath], { env });
190+
191+
CHILD_PROCESSES.add(child);
192+
193+
child.on('close', () => {
194+
hasExited = true;
195+
});
196+
197+
// Pass error to done to end the test quickly
198+
child.on('error', e => {
199+
// eslint-disable-next-line no-console
200+
if (process.env.DEBUG) console.log('scenario error', e);
201+
complete(e);
202+
});
202203

203-
let splitIndex = -1;
204-
while ((splitIndex = buffer.indexOf(0xa)) >= 0) {
205-
const line = buffer.subarray(0, splitIndex).toString();
206-
buffer = Buffer.from(buffer.subarray(splitIndex + 1));
207-
tryParseLine(line);
204+
function tryParseEnvelopeFromStdoutLine(line: string): void {
205+
// Lines can have leading '[something] [{' which we need to remove
206+
const cleanedLine = line.replace(/^.*?] \[{"/, '[{"');
207+
208+
// See if we have a port message
209+
if (cleanedLine.startsWith('{"port":')) {
210+
const { port } = JSON.parse(cleanedLine) as { port: number };
211+
scenarioServerPort = port;
212+
return;
213+
}
214+
215+
// Skip any lines that don't start with envelope JSON
216+
if (!cleanedLine.startsWith('[{')) {
217+
return;
218+
}
219+
220+
try {
221+
const envelope = JSON.parse(cleanedLine) as Envelope;
222+
newEnvelope(envelope);
223+
} catch (_) {
224+
//
225+
}
208226
}
227+
228+
let buffer = Buffer.alloc(0);
229+
child.stdout.on('data', (data: Buffer) => {
230+
// This is horribly memory inefficient but it's only for tests
231+
buffer = Buffer.concat([buffer, data]);
232+
233+
let splitIndex = -1;
234+
while ((splitIndex = buffer.indexOf(0xa)) >= 0) {
235+
const line = buffer.subarray(0, splitIndex).toString();
236+
buffer = Buffer.from(buffer.subarray(splitIndex + 1));
237+
// eslint-disable-next-line no-console
238+
if (process.env.DEBUG) console.log('line', line);
239+
tryParseEnvelopeFromStdoutLine(line);
240+
}
241+
});
209242
});
210243

211244
return {
@@ -218,13 +251,13 @@ export function createRunner(...paths: string[]) {
218251
headers: Record<string, string> = {},
219252
): Promise<T | undefined> {
220253
try {
221-
await waitForServerPort();
254+
await waitFor(() => scenarioServerPort !== undefined);
222255
} catch (e) {
223-
done?.(e);
256+
complete(e as Error);
224257
return undefined;
225258
}
226259

227-
const url = `http://localhost:${serverPort}${path}`;
260+
const url = `http://localhost:${scenarioServerPort}${path}`;
228261
if (method === 'get') {
229262
return (await axios.get(url, { headers })).data;
230263
} else {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { AddressInfo } from 'net';
2+
import { TextDecoder, TextEncoder } from 'util';
3+
import type { Envelope } from '@sentry/types';
4+
import { parseEnvelope } from '@sentry/utils';
5+
import express from 'express';
6+
7+
/**
8+
* Creates a basic Sentry server that accepts POST to the envelope endpoint
9+
*
10+
* This does no checks on the envelope, it just calls the callback if it managed to parse an envelope from the raw POST
11+
* body data.
12+
*/
13+
export function createBasicSentryServer(onEnvelope: (env: Envelope) => void): Promise<number> {
14+
const app = express();
15+
app.use(express.raw({ type: () => true, inflate: true, limit: '100mb' }));
16+
app.post('/api/:id/envelope/', (req, res) => {
17+
try {
18+
const env = parseEnvelope(req.body as Buffer, new TextEncoder(), new TextDecoder());
19+
onEnvelope(env);
20+
} catch (e) {
21+
// eslint-disable-next-line no-console
22+
console.error(e);
23+
}
24+
25+
res.status(200).send();
26+
});
27+
28+
return new Promise(resolve => {
29+
const server = app.listen(0, () => {
30+
const address = server.address() as AddressInfo;
31+
resolve(address.port);
32+
});
33+
});
34+
}

yarn.lock

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8046,6 +8046,16 @@ argparse@^2.0.1:
80468046
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
80478047
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
80488048

8049+
args@^5.0.3:
8050+
version "5.0.3"
8051+
resolved "https://registry.yarnpkg.com/args/-/args-5.0.3.tgz#943256db85021a85684be2f0882f25d796278702"
8052+
integrity sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==
8053+
dependencies:
8054+
camelcase "5.0.0"
8055+
chalk "2.4.2"
8056+
leven "2.1.0"
8057+
mri "1.1.4"
8058+
80498059
80508060
version "0.0.2"
80518061
resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab"
@@ -9423,6 +9433,11 @@ base@^0.11.1:
94239433
mixin-deep "^1.2.0"
94249434
pascalcase "^0.1.1"
94259435

9436+
9437+
version "0.0.2-1"
9438+
resolved "https://registry.yarnpkg.com/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz#f1ea575979b27af6a411921d6ff8793d9117347f"
9439+
integrity sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==
9440+
94269441
basic-auth@~2.0.1:
94279442
version "2.0.1"
94289443
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
@@ -10698,6 +10713,11 @@ camelcase-keys@^6.2.2:
1069810713
map-obj "^4.0.0"
1069910714
quick-lru "^4.0.1"
1070010715

10716+
10717+
version "5.0.0"
10718+
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
10719+
integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
10720+
1070110721
[email protected], camelcase@^5.0.0, camelcase@^5.3.1:
1070210722
version "5.3.1"
1070310723
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@@ -20019,6 +20039,11 @@ less@^4.1.0:
2001920039
needle "^3.1.0"
2002020040
source-map "~0.6.0"
2002120041

20042+
20043+
version "2.1.0"
20044+
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
20045+
integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==
20046+
2002220047
leven@^3.1.0:
2002320048
version "3.1.0"
2002420049
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
@@ -22136,6 +22161,11 @@ move-concurrently@^1.0.1:
2213622161
rimraf "^2.5.4"
2213722162
run-queue "^1.0.3"
2213822163

22164+
22165+
version "1.1.4"
22166+
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a"
22167+
integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==
22168+
2213922169
mri@^1.1.0:
2214022170
version "1.2.0"
2214122171
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
@@ -25888,6 +25918,15 @@ proxy-from-env@^1.1.0:
2588825918
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
2588925919
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
2589025920

25921+
proxy@^2.1.1:
25922+
version "2.1.1"
25923+
resolved "https://registry.yarnpkg.com/proxy/-/proxy-2.1.1.tgz#45f9b307508ffcae12bdc71678d44a4ab79cbf8b"
25924+
integrity sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==
25925+
dependencies:
25926+
args "^5.0.3"
25927+
basic-auth-parser "0.0.2-1"
25928+
debug "^4.3.4"
25929+
2589125930
prr@~1.0.1:
2589225931
version "1.0.1"
2589325932
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"

0 commit comments

Comments
 (0)