123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- import { _optionalChain } from '@sentry/utils';
- import { URL } from 'url';
- import { NODE_VERSION } from '../../nodeVersion.js';
- /**
- * Assembles a URL that's passed to the users to filter on.
- * It can include raw (potentially PII containing) data, which we'll allow users to access to filter
- * but won't include in spans or breadcrumbs.
- *
- * @param requestOptions RequestOptions object containing the component parts for a URL
- * @returns Fully-formed URL
- */
- // TODO (v8): This function should include auth, query and fragment (it's breaking, so we need to wait for v8)
- function extractRawUrl(requestOptions) {
- const { protocol, hostname, port } = parseRequestOptions(requestOptions);
- const path = requestOptions.path ? requestOptions.path : '/';
- return `${protocol}//${hostname}${port}${path}`;
- }
- /**
- * Assemble a URL to be used for breadcrumbs and spans.
- *
- * @param requestOptions RequestOptions object containing the component parts for a URL
- * @returns Fully-formed URL
- */
- function extractUrl(requestOptions) {
- const { protocol, hostname, port } = parseRequestOptions(requestOptions);
- const path = requestOptions.pathname || '/';
- // always filter authority, see https://develop.sentry.dev/sdk/data-handling/#structuring-data
- const authority = requestOptions.auth ? redactAuthority(requestOptions.auth) : '';
- return `${protocol}//${authority}${hostname}${port}${path}`;
- }
- function redactAuthority(auth) {
- const [user, password] = auth.split(':');
- return `${user ? '[Filtered]' : ''}:${password ? '[Filtered]' : ''}@`;
- }
- /**
- * Handle various edge cases in the span description (for spans representing http(s) requests).
- *
- * @param description current `description` property of the span representing the request
- * @param requestOptions Configuration data for the request
- * @param Request Request object
- *
- * @returns The cleaned description
- */
- function cleanSpanDescription(
- description,
- requestOptions,
- request,
- ) {
- // nothing to clean
- if (!description) {
- return description;
- }
- // eslint-disable-next-line prefer-const
- let [method, requestUrl] = description.split(' ');
- // superagent sticks the protocol in a weird place (we check for host because if both host *and* protocol are missing,
- // we're likely dealing with an internal route and this doesn't apply)
- if (requestOptions.host && !requestOptions.protocol) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
- requestOptions.protocol = _optionalChain([(request ), 'optionalAccess', _ => _.agent, 'optionalAccess', _2 => _2.protocol]); // worst comes to worst, this is undefined and nothing changes
- // This URL contains the filtered authority ([filtered]:[filtered]@example.com) but no fragment or query params
- requestUrl = extractUrl(requestOptions);
- }
- // internal routes can end up starting with a triple slash rather than a single one
- if (_optionalChain([requestUrl, 'optionalAccess', _3 => _3.startsWith, 'call', _4 => _4('///')])) {
- requestUrl = requestUrl.slice(2);
- }
- return `${method} ${requestUrl}`;
- }
- // the node types are missing a few properties which node's `urlToOptions` function spits out
- /**
- * Convert a URL object into a RequestOptions object.
- *
- * Copied from Node's internals (where it's used in http(s).request() and http(s).get()), modified only to use the
- * RequestOptions type above.
- *
- * See https://github.com/nodejs/node/blob/master/lib/internal/url.js.
- */
- function urlToOptions(url) {
- const options = {
- protocol: url.protocol,
- hostname:
- typeof url.hostname === 'string' && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname,
- hash: url.hash,
- search: url.search,
- pathname: url.pathname,
- path: `${url.pathname || ''}${url.search || ''}`,
- href: url.href,
- };
- if (url.port !== '') {
- options.port = Number(url.port);
- }
- if (url.username || url.password) {
- options.auth = `${url.username}:${url.password}`;
- }
- return options;
- }
- /**
- * Normalize inputs to `http(s).request()` and `http(s).get()`.
- *
- * Legal inputs to `http(s).request()` and `http(s).get()` can take one of ten forms:
- * [ RequestOptions | string | URL ],
- * [ RequestOptions | string | URL, RequestCallback ],
- * [ string | URL, RequestOptions ], and
- * [ string | URL, RequestOptions, RequestCallback ].
- *
- * This standardizes to one of two forms: [ RequestOptions ] and [ RequestOptions, RequestCallback ]. A similar thing is
- * done as the first step of `http(s).request()` and `http(s).get()`; this just does it early so that we can interact
- * with the args in a standard way.
- *
- * @param requestArgs The inputs to `http(s).request()` or `http(s).get()`, as an array.
- *
- * @returns Equivalent args of the form [ RequestOptions ] or [ RequestOptions, RequestCallback ].
- */
- function normalizeRequestArgs(
- httpModule,
- requestArgs,
- ) {
- let callback, requestOptions;
- // pop off the callback, if there is one
- if (typeof requestArgs[requestArgs.length - 1] === 'function') {
- callback = requestArgs.pop() ;
- }
- // create a RequestOptions object of whatever's at index 0
- if (typeof requestArgs[0] === 'string') {
- requestOptions = urlToOptions(new URL(requestArgs[0]));
- } else if (requestArgs[0] instanceof URL) {
- requestOptions = urlToOptions(requestArgs[0]);
- } else {
- requestOptions = requestArgs[0];
- try {
- const parsed = new URL(
- requestOptions.path || '',
- `${requestOptions.protocol || 'http:'}//${requestOptions.hostname}`,
- );
- requestOptions = {
- pathname: parsed.pathname,
- search: parsed.search,
- hash: parsed.hash,
- ...requestOptions,
- };
- } catch (e) {
- // ignore
- }
- }
- // if the options were given separately from the URL, fold them in
- if (requestArgs.length === 2) {
- requestOptions = { ...requestOptions, ...requestArgs[1] };
- }
- // Figure out the protocol if it's currently missing
- if (requestOptions.protocol === undefined) {
- // Worst case we end up populating protocol with undefined, which it already is
- /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any */
- // NOTE: Prior to Node 9, `https` used internals of `http` module, thus we don't patch it.
- // Because of that, we cannot rely on `httpModule` to provide us with valid protocol,
- // as it will always return `http`, even when using `https` module.
- //
- // See test/integrations/http.test.ts for more details on Node <=v8 protocol issue.
- if (NODE_VERSION.major > 8) {
- requestOptions.protocol =
- _optionalChain([(_optionalChain([httpModule, 'optionalAccess', _5 => _5.globalAgent]) ), 'optionalAccess', _6 => _6.protocol]) ||
- _optionalChain([(requestOptions.agent ), 'optionalAccess', _7 => _7.protocol]) ||
- _optionalChain([(requestOptions._defaultAgent ), 'optionalAccess', _8 => _8.protocol]);
- } else {
- requestOptions.protocol =
- _optionalChain([(requestOptions.agent ), 'optionalAccess', _9 => _9.protocol]) ||
- _optionalChain([(requestOptions._defaultAgent ), 'optionalAccess', _10 => _10.protocol]) ||
- _optionalChain([(_optionalChain([httpModule, 'optionalAccess', _11 => _11.globalAgent]) ), 'optionalAccess', _12 => _12.protocol]);
- }
- /* eslint-enable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any */
- }
- // return args in standardized form
- if (callback) {
- return [requestOptions, callback];
- } else {
- return [requestOptions];
- }
- }
- function parseRequestOptions(requestOptions)
- {
- const protocol = requestOptions.protocol || '';
- const hostname = requestOptions.hostname || requestOptions.host || '';
- // Don't log standard :80 (http) and :443 (https) ports to reduce the noise
- // Also don't add port if the hostname already includes a port
- const port =
- !requestOptions.port || requestOptions.port === 80 || requestOptions.port === 443 || /^(.*):(\d+)$/.test(hostname)
- ? ''
- : `:${requestOptions.port}`;
- return { protocol, hostname, port };
- }
- export { cleanSpanDescription, extractRawUrl, extractUrl, normalizeRequestArgs, urlToOptions };
- //# sourceMappingURL=http.js.map
|