123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- import { _nullishCoalesce } from '@sentry/utils';
- import * as http from 'http';
- import * as https from 'https';
- import { Readable } from 'stream';
- import { URL } from 'url';
- import { createGzip } from 'zlib';
- import { createTransport } from '@sentry/core';
- import { consoleSandbox } from '@sentry/utils';
- import { HttpsProxyAgent } from '../proxy/index.js';
- // Estimated maximum size for reasonable standalone event
- const GZIP_THRESHOLD = 1024 * 32;
- /**
- * Gets a stream from a Uint8Array or string
- * Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0
- */
- function streamFromBody(body) {
- return new Readable({
- read() {
- this.push(body);
- this.push(null);
- },
- });
- }
- /**
- * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.
- */
- function makeNodeTransport(options) {
- let urlSegments;
- try {
- urlSegments = new URL(options.url);
- } catch (e) {
- consoleSandbox(() => {
- // eslint-disable-next-line no-console
- console.warn(
- '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',
- );
- });
- return createTransport(options, () => Promise.resolve({}));
- }
- const isHttps = urlSegments.protocol === 'https:';
- // Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`
- // Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`
- const proxy = applyNoProxyOption(
- urlSegments,
- options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,
- );
- const nativeHttpModule = isHttps ? https : http;
- const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;
- // TODO(v7): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node
- // versions(>= 8) as they had memory leaks when using it: #2555
- const agent = proxy
- ? (new HttpsProxyAgent(proxy) )
- : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });
- const requestExecutor = createRequestExecutor(options, _nullishCoalesce(options.httpModule, () => ( nativeHttpModule)), agent);
- return createTransport(options, requestExecutor);
- }
- /**
- * Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.
- *
- * @param transportUrl The URL the transport intends to send events to.
- * @param proxy The client configured proxy.
- * @returns A proxy the transport should use.
- */
- function applyNoProxyOption(transportUrlSegments, proxy) {
- const { no_proxy } = process.env;
- const urlIsExemptFromProxy =
- no_proxy &&
- no_proxy
- .split(',')
- .some(
- exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),
- );
- if (urlIsExemptFromProxy) {
- return undefined;
- } else {
- return proxy;
- }
- }
- /**
- * Creates a RequestExecutor to be used with `createTransport`.
- */
- function createRequestExecutor(
- options,
- httpModule,
- agent,
- ) {
- const { hostname, pathname, port, protocol, search } = new URL(options.url);
- return function makeRequest(request) {
- return new Promise((resolve, reject) => {
- let body = streamFromBody(request.body);
- const headers = { ...options.headers };
- if (request.body.length > GZIP_THRESHOLD) {
- headers['content-encoding'] = 'gzip';
- body = body.pipe(createGzip());
- }
- const req = httpModule.request(
- {
- method: 'POST',
- agent,
- headers,
- hostname,
- path: `${pathname}${search}`,
- port,
- protocol,
- ca: options.caCerts,
- },
- res => {
- res.on('data', () => {
- // Drain socket
- });
- res.on('end', () => {
- // Drain socket
- });
- res.setEncoding('utf8');
- // "Key-value pairs of header names and values. Header names are lower-cased."
- // https://nodejs.org/api/http.html#http_message_headers
- const retryAfterHeader = _nullishCoalesce(res.headers['retry-after'], () => ( null));
- const rateLimitsHeader = _nullishCoalesce(res.headers['x-sentry-rate-limits'], () => ( null));
- resolve({
- statusCode: res.statusCode,
- headers: {
- 'retry-after': retryAfterHeader,
- 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader,
- },
- });
- },
- );
- req.on('error', reject);
- body.pipe(req);
- });
- };
- }
- export { makeNodeTransport };
- //# sourceMappingURL=http.js.map
|