Skip to content

Commit 8aef214

Browse files
committed
move extractRequestData to @sentry/utils
1 parent 819270a commit 8aef214

File tree

3 files changed

+111
-96
lines changed

3 files changed

+111
-96
lines changed

packages/node/src/handlers.ts

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core';
44
import { Span } from '@sentry/tracing';
55
import { Event } from '@sentry/types';
6-
import { forget, isPlainObject, isString, logger, normalize } from '@sentry/utils';
7-
import * as cookie from 'cookie';
6+
import { extractNodeRequestData, forget, isPlainObject, isString, logger } from '@sentry/utils';
87
import * as domain from 'domain';
98
import * as http from 'http';
109
import * as os from 'os';
@@ -120,94 +119,6 @@ function extractTransaction(req: { [key: string]: any }, type: boolean | Transac
120119
}
121120
}
122121

123-
/** Default request keys that'll be used to extract data from the request */
124-
const DEFAULT_REQUEST_KEYS = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
125-
126-
/**
127-
* Normalizes data from the request object, accounting for framework differences.
128-
*
129-
* @param req The request object from which to extract data
130-
* @param keys An optional array of keys to include in the normalized data. Defaults to DEFAULT_REQUEST_KEYS if not
131-
* provided.
132-
* @returns An object containing normalized request data
133-
*/
134-
export function extractRequestData(
135-
req: { [key: string]: any },
136-
keys: string[] = DEFAULT_REQUEST_KEYS,
137-
): { [key: string]: string } {
138-
const requestData: { [key: string]: any } = {};
139-
140-
// headers:
141-
// node, express: req.headers
142-
// koa: req.header
143-
const headers = (req.headers || req.header || {}) as {
144-
host?: string;
145-
cookie?: string;
146-
};
147-
// method:
148-
// node, express, koa: req.method
149-
const method = req.method;
150-
// host:
151-
// express: req.hostname in > 4 and req.host in < 4
152-
// koa: req.host
153-
// node: req.headers.host
154-
const host = req.hostname || req.host || headers.host || '<no host>';
155-
// protocol:
156-
// node: <n/a>
157-
// express, koa: req.protocol
158-
const protocol =
159-
req.protocol === 'https' || req.secure || ((req.socket || {}) as { encrypted?: boolean }).encrypted
160-
? 'https'
161-
: 'http';
162-
// url (including path and query string):
163-
// node, express: req.originalUrl
164-
// koa: req.url
165-
const originalUrl = (req.originalUrl || req.url) as string;
166-
// absolute url
167-
const absoluteUrl = `${protocol}://${host}${originalUrl}`;
168-
169-
keys.forEach(key => {
170-
switch (key) {
171-
case 'headers':
172-
requestData.headers = headers;
173-
break;
174-
case 'method':
175-
requestData.method = method;
176-
break;
177-
case 'url':
178-
requestData.url = absoluteUrl;
179-
break;
180-
case 'cookies':
181-
// cookies:
182-
// node, express, koa: req.headers.cookie
183-
requestData.cookies = cookie.parse(headers.cookie || '');
184-
break;
185-
case 'query_string':
186-
// query string:
187-
// node: req.url (raw)
188-
// express, koa: req.query
189-
requestData.query_string = url.parse(originalUrl || '', false).query;
190-
break;
191-
case 'data':
192-
if (method === 'GET' || method === 'HEAD') {
193-
break;
194-
}
195-
// body data:
196-
// node, express, koa: req.body
197-
if (req.body !== undefined) {
198-
requestData.data = isString(req.body) ? req.body : JSON.stringify(normalize(req.body));
199-
}
200-
break;
201-
default:
202-
if ({}.hasOwnProperty.call(req, key)) {
203-
requestData[key] = (req as { [key: string]: any })[key];
204-
}
205-
}
206-
});
207-
208-
return requestData;
209-
}
210-
211122
/** Default user keys that'll be used to extract data from the request */
212123
const DEFAULT_USER_KEYS = ['id', 'username', 'email'];
213124

@@ -288,7 +199,7 @@ export function parseRequest(
288199
if (options.request) {
289200
event.request = {
290201
...event.request,
291-
...extractRequestData(req), // extract default keys given in DEFAULT_REQUEST_KEYS
202+
...extractNodeRequestData(req),
292203
};
293204
}
294205

packages/tracing/src/hubextensions.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { getActiveDomain, getMainCarrier, Hub } from '@sentry/hub';
2-
import { Handlers } from '@sentry/node';
32
import { SampleContext, TransactionContext } from '@sentry/types';
4-
import { hasTracingEnabled, isInstanceOf, isNodeEnv, logger } from '@sentry/utils';
5-
import * as http from 'http'; // TODO - is this okay, or do we need to export the type we need from our node SDK?
3+
import {
4+
dynamicRequire,
5+
extractNodeRequestData,
6+
hasTracingEnabled,
7+
isInstanceOf,
8+
isNodeEnv,
9+
logger,
10+
} from '@sentry/utils';
611

712
import { registerErrorInstrumentation } from './errors';
813
import { IdleTransaction } from './idletransaction';
@@ -89,15 +94,18 @@ function getDefaultSampleContext(): SampleContext {
8994
if (isNodeEnv()) {
9095
// look at koa TODO
9196
const domain = getActiveDomain();
97+
const nodeHttpModule = dynamicRequire(module, 'http');
98+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
99+
const requestType = nodeHttpModule.IncomingMessage;
92100
if (domain) {
93101
// TODO is the below true?
94102
// for all node servers that we currently support, we store the incoming request object (which is an instance of
95103
// http.IncomingMessage) on the domain the domain is stored as an array, so our only way to find the request is to
96104
// iterate through the array and compare types
97105
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
98-
const request = domain.members.find((member: any) => isInstanceOf(member, http.IncomingMessage));
106+
const request = domain.members.find((member: any) => isInstanceOf(member, requestType));
99107
if (request) {
100-
defaultSampleContext.request = Handlers.extractRequestData(request);
108+
defaultSampleContext.request = extractNodeRequestData(request);
101109
}
102110
}
103111
}

packages/utils/src/misc.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Event, Integration, Options, StackFrame, WrappedFunction } from '@sentry/types';
33

44
import { isString } from './is';
5+
import { normalize } from './object';
56
import { snipLine } from './string';
67

78
/** Internal */
@@ -519,3 +520,98 @@ export function addContextToFrame(lines: string[], frame: StackFrame, linesOfCon
519520
export function hasTracingEnabled(options: Options): boolean {
520521
return !!options.tracesSampleRate || !!options.tracesSampler;
521522
}
523+
524+
/** Default request keys that'll be used to extract data from the request */
525+
const DEFAULT_REQUEST_KEYS = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
526+
527+
/**
528+
* Normalizes data from the request object, accounting for framework differences.
529+
*
530+
* @param req The request object from which to extract data
531+
* @param keys An optional array of keys to include in the normalized data. Defaults to DEFAULT_REQUEST_KEYS if not
532+
* provided.
533+
* @returns An object containing normalized request data
534+
*/
535+
export function extractNodeRequestData(
536+
req: { [key: string]: any },
537+
keys: string[] = DEFAULT_REQUEST_KEYS,
538+
): { [key: string]: string } {
539+
// make sure we can safely use dynamicRequire below
540+
if (!isNodeEnv()) {
541+
throw new Error("Can't get node request data outside of a node environment");
542+
}
543+
544+
const requestData: { [key: string]: any } = {};
545+
546+
// headers:
547+
// node, express: req.headers
548+
// koa: req.header
549+
const headers = (req.headers || req.header || {}) as {
550+
host?: string;
551+
cookie?: string;
552+
};
553+
// method:
554+
// node, express, koa: req.method
555+
const method = req.method;
556+
// host:
557+
// express: req.hostname in > 4 and req.host in < 4
558+
// koa: req.host
559+
// node: req.headers.host
560+
const host = req.hostname || req.host || headers.host || '<no host>';
561+
// protocol:
562+
// node: <n/a>
563+
// express, koa: req.protocol
564+
const protocol =
565+
req.protocol === 'https' || req.secure || ((req.socket || {}) as { encrypted?: boolean }).encrypted
566+
? 'https'
567+
: 'http';
568+
// url (including path and query string):
569+
// node, express: req.originalUrl
570+
// koa: req.url
571+
const originalUrl = (req.originalUrl || req.url) as string;
572+
// absolute url
573+
const absoluteUrl = `${protocol}://${host}${originalUrl}`;
574+
575+
keys.forEach(key => {
576+
switch (key) {
577+
case 'headers':
578+
requestData.headers = headers;
579+
break;
580+
case 'method':
581+
requestData.method = method;
582+
break;
583+
case 'url':
584+
requestData.url = absoluteUrl;
585+
break;
586+
case 'cookies':
587+
// cookies:
588+
// node, express, koa: req.headers.cookie
589+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
590+
requestData.cookies = dynamicRequire(module, 'cookie').parse(headers.cookie || '');
591+
break;
592+
case 'query_string':
593+
// query string:
594+
// node: req.url (raw)
595+
// express, koa: req.query
596+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
597+
requestData.query_string = dynamicRequire(module, 'url').parse(originalUrl || '', false).query;
598+
break;
599+
case 'data':
600+
if (method === 'GET' || method === 'HEAD') {
601+
break;
602+
}
603+
// body data:
604+
// node, express, koa: req.body
605+
if (req.body !== undefined) {
606+
requestData.data = isString(req.body) ? req.body : JSON.stringify(normalize(req.body));
607+
}
608+
break;
609+
default:
610+
if ({}.hasOwnProperty.call(req, key)) {
611+
requestData[key] = (req as { [key: string]: any })[key];
612+
}
613+
}
614+
});
615+
616+
return requestData;
617+
}

0 commit comments

Comments
 (0)