@sentry/node
Version:
Sentry Node SDK using OpenTelemetry for performance instrumentation
108 lines (105 loc) • 3.73 kB
JavaScript
import * as http from 'node:http';
import * as https from 'node:https';
import { Readable } from 'node:stream';
import { createGzip } from 'node:zlib';
import { consoleSandbox, createTransport, suppressTracing } from '@sentry/core';
import { HttpsProxyAgent } from '../proxy/index.js';
const GZIP_THRESHOLD = 1024 * 32;
function streamFromBody(body) {
return new Readable({
read() {
this.push(body);
this.push(null);
}
});
}
function makeNodeTransport(options) {
let urlSegments;
try {
urlSegments = new URL(options.url);
} catch {
consoleSandbox(() => {
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:";
const proxy = applyNoProxyOption(
urlSegments,
options.proxy || (isHttps ? process.env.https_proxy : void 0) || process.env.http_proxy
);
const nativeHttpModule = isHttps ? https : http;
const keepAlive = options.keepAlive ?? true;
const agent = proxy ? new HttpsProxyAgent(proxy) : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2e3 });
const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent);
return createTransport(options, requestExecutor);
}
function applyNoProxyOption(transportUrlSegments, proxy) {
const { no_proxy } = process.env;
const urlIsExemptFromProxy = no_proxy?.split(",").some(
(exemption) => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption)
);
if (urlIsExemptFromProxy) {
return void 0;
} else {
return proxy;
}
}
function createRequestExecutor(options, httpModule, agent) {
const { hostname, pathname, port, protocol, search } = new URL(options.url);
return function makeRequest(request) {
const sendRequest = (canRetry) => new Promise((resolve, reject) => {
suppressTracing(() => {
let body = streamFromBody(request.body);
const headers = { ...options.headers };
if (request.body.length > GZIP_THRESHOLD) {
headers["content-encoding"] = "gzip";
body = body.pipe(createGzip());
}
const hostnameIsIPv6 = hostname.startsWith("[");
const req = httpModule.request(
{
method: "POST",
agent,
headers,
// Remove "[" and "]" from IPv6 hostnames
hostname: hostnameIsIPv6 ? hostname.slice(1, -1) : hostname,
path: `${pathname}${search}`,
port,
protocol,
ca: options.caCerts
},
(res) => {
res.on("data", () => {
});
res.on("end", () => {
});
res.setEncoding("utf8");
const retryAfterHeader = res.headers["retry-after"] ?? null;
const rateLimitsHeader = res.headers["x-sentry-rate-limits"] ?? null;
resolve({
statusCode: res.statusCode,
headers: {
"retry-after": retryAfterHeader,
"x-sentry-rate-limits": Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] || null : rateLimitsHeader
}
});
}
);
req.on("error", (error) => {
if (canRetry && req.reusedSocket && error.code === "ECONNRESET") {
resolve(sendRequest(false));
} else {
reject(error);
}
});
body.pipe(req);
});
});
return sendRequest(true);
};
}
export { makeNodeTransport };
//# sourceMappingURL=http.js.map