Skip to content

Commit 72aa247

Browse files
authored
Merge branch 'master' into tag-lcp-elements
2 parents 3b969a9 + 888275c commit 72aa247

File tree

11 files changed

+231
-72
lines changed

11 files changed

+231
-72
lines changed

packages/browser/src/sdk.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -189,23 +189,25 @@ function startSessionTracking(): void {
189189

190190
const hub = getCurrentHub();
191191

192-
if ('startSession' in hub) {
193-
// The only way for this to be false is for there to be a version mismatch between @sentry/browser (>= 6.0.0) and
194-
// @sentry/hub (< 5.27.0). In the simple case, there won't ever be such a mismatch, because the two packages are
195-
// pinned at the same version in package.json, but there are edge cases where it's possible'. See
196-
// https://github.com/getsentry/sentry-javascript/issues/3234 and
197-
// https://github.com/getsentry/sentry-javascript/issues/3207.
198-
199-
hub.startSession();
200-
hub.captureSession();
201-
202-
// We want to create a session for every navigation as well
203-
addInstrumentationHandler({
204-
callback: () => {
205-
hub.startSession();
206-
hub.captureSession();
207-
},
208-
type: 'history',
209-
});
192+
// The only way for this to be false is for there to be a version mismatch between @sentry/browser (>= 6.0.0) and
193+
// @sentry/hub (< 5.27.0). In the simple case, there won't ever be such a mismatch, because the two packages are
194+
// pinned at the same version in package.json, but there are edge cases where it's possible. See
195+
// https://github.com/getsentry/sentry-javascript/issues/3207 and
196+
// https://github.com/getsentry/sentry-javascript/issues/3234 and
197+
// https://github.com/getsentry/sentry-javascript/issues/3278.
198+
if (typeof hub.startSession !== 'function' || typeof hub.captureSession !== 'function') {
199+
return;
210200
}
201+
202+
hub.startSession();
203+
hub.captureSession();
204+
205+
// We want to create a session for every navigation as well
206+
addInstrumentationHandler({
207+
callback: () => {
208+
hub.startSession();
209+
hub.captureSession();
210+
},
211+
type: 'history',
212+
});
211213
}

packages/node/src/sdk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export async function close(timeout?: number): Promise<boolean> {
149149
}
150150

151151
/**
152-
* A function that returns a Sentry release string dynamically from env variables
152+
* Returns a release dynamically from environment variables.
153153
*/
154154
export function getSentryRelease(): string | undefined {
155155
// Always read first as Sentry takes this as precedence

packages/node/src/transports/base.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { API, eventToSentryRequest, SDK_VERSION } from '@sentry/core';
2-
import { Event, Response, Status, Transport, TransportOptions } from '@sentry/types';
2+
import { DsnProtocol, Event, Response, Status, Transport, TransportOptions } from '@sentry/types';
33
import { logger, parseRetryAfterHeader, PromiseBuffer, SentryError } from '@sentry/utils';
44
import * as fs from 'fs';
55
import * as http from 'http';
@@ -70,6 +70,34 @@ export abstract class BaseTransport implements Transport {
7070
return this._buffer.drain(timeout);
7171
}
7272

73+
/**
74+
* Extracts proxy settings from client options and env variables.
75+
*
76+
* Honors `no_proxy` env variable with the highest priority to allow for hosts exclusion.
77+
*
78+
* An order of priority for available protocols is:
79+
* `http` => `options.httpProxy` | `process.env.http_proxy`
80+
* `https` => `options.httpsProxy` | `options.httpProxy` | `process.env.https_proxy` | `process.env.http_proxy`
81+
*/
82+
protected _getProxy(protocol: DsnProtocol): string | undefined {
83+
const { no_proxy, http_proxy, https_proxy } = process.env;
84+
const { httpProxy, httpsProxy } = this.options;
85+
const proxy = protocol === 'http' ? httpProxy || http_proxy : httpsProxy || httpProxy || https_proxy || http_proxy;
86+
87+
if (!no_proxy) {
88+
return proxy;
89+
}
90+
91+
const { host, port } = this._api.getDsn();
92+
for (const np of no_proxy.split(',')) {
93+
if (host.endsWith(np) || `${host}:${port}`.endsWith(np)) {
94+
return;
95+
}
96+
}
97+
98+
return proxy;
99+
}
100+
73101
/** Returns a build request option object used by request */
74102
protected _getRequestOptions(uri: url.URL): http.RequestOptions | https.RequestOptions {
75103
const headers = {

packages/node/src/transports/http.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class HTTPTransport extends BaseTransport {
99
/** Create a new instance and set this.agent */
1010
public constructor(public options: TransportOptions) {
1111
super(options);
12-
const proxy = options.httpProxy || process.env.http_proxy;
12+
const proxy = this._getProxy('http');
1313
this.module = http;
1414
this.client = proxy
1515
? (new (require('https-proxy-agent'))(proxy) as http.Agent)

packages/node/src/transports/https.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class HTTPSTransport extends BaseTransport {
99
/** Create a new instance and set this.agent */
1010
public constructor(public options: TransportOptions) {
1111
super(options);
12-
const proxy = options.httpsProxy || options.httpProxy || process.env.https_proxy || process.env.http_proxy;
12+
const proxy = this._getProxy('https');
1313
this.module = https;
1414
this.client = proxy
1515
? (new (require('https-proxy-agent'))(proxy) as https.Agent)

packages/node/test/manual/apm-transaction/main.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ app.get('/trace', async (_req, res, next) => {
105105
app.use(Tracing.finish());
106106
app.use(Sentry.Handlers.errorHandler());
107107

108-
const server = app.listen(1231, () => {
109-
http.get('http://localhost:1231/trace', res => {
108+
const server = app.listen(0, () => {
109+
const port = server.address().port;
110+
http.get(`http://localhost:${port}/trace`, res => {
110111
server.close();
111112
});
112113
});

packages/node/test/manual/express-scope-separation/start.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class DummyTransport {
1818

1919
if (!remaining) {
2020
console.error('SUCCESS: All scopes contain correct tags');
21+
server.close();
2122
process.exit(0);
2223
}
2324

@@ -83,8 +84,9 @@ app.get('/baz', req => {
8384

8485
app.use(Sentry.Handlers.errorHandler());
8586

86-
app.listen(1121);
87-
88-
http.get('http://localhost:1121/foo');
89-
http.get('http://localhost:1121/bar');
90-
http.get('http://localhost:1121/baz');
87+
const server = app.listen(0, () => {
88+
const port = server.address().port;
89+
http.get(`http://localhost:${port}/foo`);
90+
http.get(`http://localhost:${port}/bar`);
91+
http.get(`http://localhost:${port}/baz`);
92+
});

packages/node/test/manual/memory-leak/express-patient.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ app.use((err, req, res, next) => {
146146
return res.status(500).send('oh no there was an error: ' + err.message);
147147
});
148148

149-
const server = app.listen(5140, () => {
150-
process._rawDebug('patient is waiting to be poked on port 5140');
149+
const server = app.listen(0, () => {
150+
const port = server.address().port;
151+
process._rawDebug(`patient is waiting to be poked on port ${port}`);
151152
memwatch.gc();
152153
});

packages/node/test/transports/http.test.ts

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TransportOptions } from '@sentry/types';
22
import { SentryError } from '@sentry/utils';
3+
import * as http from 'http';
34
import * as HttpsProxyAgent from 'https-proxy-agent';
45

56
import { HTTPTransport } from '../../src/transports/http';
@@ -147,22 +148,71 @@ describe('HTTPTransport', () => {
147148
expect(requestOptions.headers).toEqual(expect.objectContaining({ a: 'b' }));
148149
});
149150

150-
test('http proxy', async () => {
151-
mockReturnCode = 200;
152-
const transport = createTransport({
153-
dsn,
154-
httpProxy: 'http://example.com:8080',
151+
describe('proxy', () => {
152+
test('can be configured through client option', async () => {
153+
const transport = createTransport({
154+
dsn,
155+
httpProxy: 'http://example.com:8080',
156+
});
157+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
158+
expect(client).toBeInstanceOf(HttpsProxyAgent);
159+
expect(client.secureProxy).toEqual(false);
160+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' }));
155161
});
156-
await transport.sendEvent({
157-
message: 'test',
162+
163+
test('can be configured through env variables option', async () => {
164+
process.env.http_proxy = 'http://example.com:8080';
165+
const transport = createTransport({
166+
dsn,
167+
httpProxy: 'http://example.com:8080',
168+
});
169+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
170+
expect(client).toBeInstanceOf(HttpsProxyAgent);
171+
expect(client.secureProxy).toEqual(false);
172+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' }));
173+
delete process.env.http_proxy;
158174
});
159175

160-
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
161-
assertBasicOptions(requestOptions);
162-
expect(requestOptions.agent).toBeInstanceOf(HttpsProxyAgent);
163-
expect(requestOptions.agent.secureProxy).toEqual(false);
164-
expect(requestOptions.agent.proxy).toEqual(
165-
expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' }),
166-
);
176+
test('client options have priority over env variables', async () => {
177+
process.env.http_proxy = 'http://env-example.com:8080';
178+
const transport = createTransport({
179+
dsn,
180+
httpProxy: 'http://example.com:8080',
181+
});
182+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
183+
expect(client).toBeInstanceOf(HttpsProxyAgent);
184+
expect(client.secureProxy).toEqual(false);
185+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'http:', port: 8080, host: 'example.com' }));
186+
delete process.env.http_proxy;
187+
});
188+
189+
test('no_proxy allows for skipping specific hosts', async () => {
190+
process.env.no_proxy = 'sentry.io';
191+
const transport = createTransport({
192+
dsn,
193+
httpProxy: 'http://example.com:8080',
194+
});
195+
expect(transport.client).toBeInstanceOf(http.Agent);
196+
});
197+
198+
test('no_proxy works with a port', async () => {
199+
process.env.http_proxy = 'http://example.com:8080';
200+
process.env.no_proxy = 'sentry.io:8989';
201+
const transport = createTransport({
202+
dsn,
203+
});
204+
expect(transport.client).toBeInstanceOf(http.Agent);
205+
delete process.env.http_proxy;
206+
});
207+
208+
test('no_proxy works with multiple comma-separated hosts', async () => {
209+
process.env.http_proxy = 'http://example.com:8080';
210+
process.env.no_proxy = 'example.com,sentry.io,wat.com:1337';
211+
const transport = createTransport({
212+
dsn,
213+
});
214+
expect(transport.client).toBeInstanceOf(http.Agent);
215+
delete process.env.http_proxy;
216+
});
167217
});
168218
});

packages/node/test/transports/https.test.ts

Lines changed: 100 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TransportOptions } from '@sentry/types';
22
import { SentryError } from '@sentry/utils';
3+
import * as https from 'https';
34
import * as HttpsProxyAgent from 'https-proxy-agent';
45

56
import { HTTPSTransport } from '../../src/transports/https';
@@ -153,36 +154,110 @@ describe('HTTPSTransport', () => {
153154
expect(requestOptions.headers).toEqual(expect.objectContaining({ a: 'b' }));
154155
});
155156

156-
test('https proxy', async () => {
157-
mockReturnCode = 200;
158-
const transport = createTransport({
159-
dsn,
160-
httpsProxy: 'https://example.com:8080',
157+
describe('proxy', () => {
158+
test('can be configured through client option', async () => {
159+
const transport = createTransport({
160+
dsn,
161+
httpsProxy: 'https://example.com:8080',
162+
});
163+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
164+
expect(client).toBeInstanceOf(HttpsProxyAgent);
165+
expect(client.secureProxy).toEqual(true);
166+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' }));
161167
});
162-
await transport.sendEvent({
163-
message: 'test',
168+
169+
test('can be configured through env variables option', async () => {
170+
process.env.https_proxy = 'https://example.com:8080';
171+
const transport = createTransport({
172+
dsn,
173+
httpsProxy: 'https://example.com:8080',
174+
});
175+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
176+
expect(client).toBeInstanceOf(HttpsProxyAgent);
177+
expect(client.secureProxy).toEqual(true);
178+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' }));
179+
delete process.env.https_proxy;
164180
});
165181

166-
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
167-
assertBasicOptions(requestOptions);
168-
expect(requestOptions.agent).toBeInstanceOf(HttpsProxyAgent);
169-
expect(requestOptions.agent.secureProxy).toEqual(true);
170-
expect(requestOptions.agent.proxy).toEqual(
171-
expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' }),
172-
);
173-
});
182+
test('https proxies have priority in client option', async () => {
183+
const transport = createTransport({
184+
dsn,
185+
httpProxy: 'http://unsecure-example.com:8080',
186+
httpsProxy: 'https://example.com:8080',
187+
});
188+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
189+
expect(client).toBeInstanceOf(HttpsProxyAgent);
190+
expect(client.secureProxy).toEqual(true);
191+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' }));
192+
});
174193

175-
test('tls certificate', async () => {
176-
mockReturnCode = 200;
177-
const transport = createTransport({
178-
caCerts: './some/path.pem',
179-
dsn,
194+
test('https proxies have priority in env variables', async () => {
195+
process.env.http_proxy = 'http://unsecure-example.com:8080';
196+
process.env.https_proxy = 'https://example.com:8080';
197+
const transport = createTransport({
198+
dsn,
199+
});
200+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
201+
expect(client).toBeInstanceOf(HttpsProxyAgent);
202+
expect(client.secureProxy).toEqual(true);
203+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' }));
204+
delete process.env.http_proxy;
205+
delete process.env.https_proxy;
180206
});
181-
await transport.sendEvent({
182-
message: 'test',
207+
208+
test('client options have priority over env variables', async () => {
209+
process.env.https_proxy = 'https://env-example.com:8080';
210+
const transport = createTransport({
211+
dsn,
212+
httpsProxy: 'https://example.com:8080',
213+
});
214+
const client = (transport.client as unknown) as { proxy: Record<string, string | number>; secureProxy: boolean };
215+
expect(client).toBeInstanceOf(HttpsProxyAgent);
216+
expect(client.secureProxy).toEqual(true);
217+
expect(client.proxy).toEqual(expect.objectContaining({ protocol: 'https:', port: 8080, host: 'example.com' }));
218+
delete process.env.https_proxy;
219+
});
220+
221+
test('no_proxy allows for skipping specific hosts', async () => {
222+
process.env.no_proxy = 'sentry.io';
223+
const transport = createTransport({
224+
dsn,
225+
httpsProxy: 'https://example.com:8080',
226+
});
227+
expect(transport.client).toBeInstanceOf(https.Agent);
228+
});
229+
230+
test('no_proxy works with a port', async () => {
231+
process.env.https_proxy = 'https://example.com:8080';
232+
process.env.no_proxy = 'sentry.io:8989';
233+
const transport = createTransport({
234+
dsn,
235+
});
236+
expect(transport.client).toBeInstanceOf(https.Agent);
237+
delete process.env.https_proxy;
238+
});
239+
240+
test('no_proxy works with multiple comma-separated hosts', async () => {
241+
process.env.http_proxy = 'https://example.com:8080';
242+
process.env.no_proxy = 'example.com,sentry.io,wat.com:1337';
243+
const transport = createTransport({
244+
dsn,
245+
});
246+
expect(transport.client).toBeInstanceOf(https.Agent);
247+
delete process.env.https_proxy;
248+
});
249+
250+
test('can configure tls certificate through client option', async () => {
251+
mockReturnCode = 200;
252+
const transport = createTransport({
253+
caCerts: './some/path.pem',
254+
dsn,
255+
});
256+
await transport.sendEvent({
257+
message: 'test',
258+
});
259+
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
260+
expect(requestOptions.ca).toEqual('mockedCert');
183261
});
184-
const requestOptions = (transport.module!.request as jest.Mock).mock.calls[0][0];
185-
assertBasicOptions(requestOptions);
186-
expect(requestOptions.ca).toEqual('mockedCert');
187262
});
188263
});

0 commit comments

Comments
 (0)