UNPKG

22.7 kBSource Map (JSON)View Raw
1{"version":3,"file":"httpclient.js","sources":["../../../src/httpclient.ts"],"sourcesContent":["import type { Event as SentryEvent, EventProcessor, Hub, Integration } from '@sentry/types';\nimport { addExceptionMechanism, fill, GLOBAL_OBJ, logger, supportsNativeFetch } from '@sentry/utils';\n\nexport type HttpStatusCodeRange = [number, number] | number;\nexport type HttpRequestTarget = string | RegExp;\ninterface HttpClientOptions {\n /**\n * HTTP status codes that should be considered failed.\n * This array can contain tuples of `[begin, end]` (both inclusive),\n * single status codes, or a combinations of both\n *\n * Example: [[500, 505], 507]\n * Default: [[500, 599]]\n */\n failedRequestStatusCodes?: HttpStatusCodeRange[];\n\n /**\n * Targets to track for failed requests.\n * This array can contain strings or regular expressions.\n *\n * Example: ['http://localhost', /api\\/.*\\/]\n * Default: [/.*\\/]\n */\n failedRequestTargets?: HttpRequestTarget[];\n}\n\n/** HTTPClient integration creates events for failed client side HTTP requests. */\nexport class HttpClient implements Integration {\n /**\n * @inheritDoc\n */\n public static id: string = 'HttpClient';\n\n /**\n * @inheritDoc\n */\n public name: string = HttpClient.id;\n\n private readonly _options: HttpClientOptions;\n\n /**\n * Returns current hub.\n */\n private _getCurrentHub?: () => Hub;\n\n /**\n * @inheritDoc\n *\n * @param options\n */\n public constructor(options?: Partial<HttpClientOptions>) {\n this._options = {\n failedRequestStatusCodes: [[500, 599]],\n failedRequestTargets: [/.*/],\n ...options,\n };\n }\n\n /**\n * @inheritDoc\n *\n * @param options\n */\n public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {\n this._getCurrentHub = getCurrentHub;\n this._wrapFetch();\n this._wrapXHR();\n }\n\n /**\n * Interceptor function for fetch requests\n *\n * @param requestInfo The Fetch API request info\n * @param response The Fetch API response\n * @param requestInit The request init object\n */\n private _fetchResponseHandler(requestInfo: RequestInfo, response: Response, requestInit?: RequestInit): void {\n if (this._getCurrentHub && this._shouldCaptureResponse(response.status, response.url)) {\n const request = new Request(requestInfo, requestInit);\n const hub = this._getCurrentHub();\n\n let requestHeaders, responseHeaders, requestCookies, responseCookies;\n\n if (hub.shouldSendDefaultPii()) {\n [{ headers: requestHeaders, cookies: requestCookies }, { headers: responseHeaders, cookies: responseCookies }] =\n [\n { cookieHeader: 'Cookie', obj: request },\n { cookieHeader: 'Set-Cookie', obj: response },\n ].map(({ cookieHeader, obj }) => {\n const headers = this._extractFetchHeaders(obj.headers);\n let cookies;\n\n try {\n const cookieString = headers[cookieHeader] || headers[cookieHeader.toLowerCase()] || undefined;\n\n if (cookieString) {\n cookies = this._parseCookieString(cookieString);\n }\n } catch (e) {\n __DEBUG_BUILD__ && logger.log(`Could not extract cookies from header ${cookieHeader}`);\n }\n\n return {\n headers,\n cookies,\n };\n });\n }\n\n const event = this._createEvent({\n url: request.url,\n method: request.method,\n status: response.status,\n requestHeaders,\n responseHeaders,\n requestCookies,\n responseCookies,\n });\n\n hub.captureEvent(event);\n }\n }\n\n /**\n * Interceptor function for XHR requests\n *\n * @param xhr The XHR request\n * @param method The HTTP method\n * @param headers The HTTP headers\n */\n private _xhrResponseHandler(xhr: XMLHttpRequest, method: string, headers: Record<string, string>): void {\n if (this._getCurrentHub && this._shouldCaptureResponse(xhr.status, xhr.responseURL)) {\n let requestHeaders, responseCookies, responseHeaders;\n const hub = this._getCurrentHub();\n\n if (hub.shouldSendDefaultPii()) {\n try {\n const cookieString = xhr.getResponseHeader('Set-Cookie') || xhr.getResponseHeader('set-cookie') || undefined;\n\n if (cookieString) {\n responseCookies = this._parseCookieString(cookieString);\n }\n } catch (e) {\n __DEBUG_BUILD__ && logger.log('Could not extract cookies from response headers');\n }\n\n try {\n responseHeaders = this._getXHRResponseHeaders(xhr);\n } catch (e) {\n __DEBUG_BUILD__ && logger.log('Could not extract headers from response');\n }\n\n requestHeaders = headers;\n }\n\n const event = this._createEvent({\n url: xhr.responseURL,\n method: method,\n status: xhr.status,\n requestHeaders,\n // Can't access request cookies from XHR\n responseHeaders,\n responseCookies,\n });\n\n hub.captureEvent(event);\n }\n }\n\n /**\n * Extracts response size from `Content-Length` header when possible\n *\n * @param headers\n * @returns The response size in bytes or undefined\n */\n private _getResponseSizeFromHeaders(headers?: Record<string, string>): number | undefined {\n if (headers) {\n const contentLength = headers['Content-Length'] || headers['content-length'];\n\n if (contentLength) {\n return parseInt(contentLength, 10);\n }\n }\n\n return undefined;\n }\n\n /**\n * Creates an object containing cookies from the given cookie string\n *\n * @param cookieString The cookie string to parse\n * @returns The parsed cookies\n */\n private _parseCookieString(cookieString: string): Record<string, string> {\n return cookieString.split('; ').reduce((acc: Record<string, string>, cookie: string) => {\n const [key, value] = cookie.split('=');\n acc[key] = value;\n return acc;\n }, {});\n }\n\n /**\n * Extracts the headers as an object from the given Fetch API request or response object\n *\n * @param headers The headers to extract\n * @returns The extracted headers as an object\n */\n private _extractFetchHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n\n headers.forEach((value, key) => {\n result[key] = value;\n });\n\n return result;\n }\n\n /**\n * Extracts the response headers as an object from the given XHR object\n *\n * @param xhr The XHR object to extract the response headers from\n * @returns The response headers as an object\n */\n private _getXHRResponseHeaders(xhr: XMLHttpRequest): Record<string, string> {\n const headers = xhr.getAllResponseHeaders();\n\n if (!headers) {\n return {};\n }\n\n return headers.split('\\r\\n').reduce((acc: Record<string, string>, line: string) => {\n const [key, value] = line.split(': ');\n acc[key] = value;\n return acc;\n }, {});\n }\n\n /**\n * Checks if the given target url is in the given list of targets\n *\n * @param target The target url to check\n * @returns true if the target url is in the given list of targets, false otherwise\n */\n private _isInGivenRequestTargets(target: string): boolean {\n if (!this._options.failedRequestTargets) {\n return false;\n }\n\n return this._options.failedRequestTargets.some((givenRequestTarget: HttpRequestTarget) => {\n if (typeof givenRequestTarget === 'string') {\n return target.includes(givenRequestTarget);\n }\n\n return givenRequestTarget.test(target);\n });\n }\n\n /**\n * Checks if the given status code is in the given range\n *\n * @param status The status code to check\n * @returns true if the status code is in the given range, false otherwise\n */\n private _isInGivenStatusRanges(status: number): boolean {\n if (!this._options.failedRequestStatusCodes) {\n return false;\n }\n\n return this._options.failedRequestStatusCodes.some((range: HttpStatusCodeRange) => {\n if (typeof range === 'number') {\n return range === status;\n }\n\n return status >= range[0] && status <= range[1];\n });\n }\n\n /**\n * Wraps `fetch` function to capture request and response data\n */\n private _wrapFetch(): void {\n if (!supportsNativeFetch()) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n\n fill(GLOBAL_OBJ, 'fetch', function (originalFetch: (...args: unknown[]) => Promise<Response>) {\n return function (this: Window, ...args: unknown[]): Promise<Response> {\n const [requestInfo, requestInit] = args as [RequestInfo, RequestInit | undefined];\n const responsePromise: Promise<Response> = originalFetch.apply(this, args);\n\n responsePromise\n .then((response: Response) => {\n self._fetchResponseHandler(requestInfo, response, requestInit);\n return response;\n })\n .catch((error: Error) => {\n throw error;\n });\n\n return responsePromise;\n };\n });\n }\n\n /**\n * Wraps XMLHttpRequest to capture request and response data\n */\n private _wrapXHR(): void {\n if (!('XMLHttpRequest' in GLOBAL_OBJ)) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n\n fill(XMLHttpRequest.prototype, 'open', function (originalOpen: (...openArgs: unknown[]) => void): () => void {\n return function (this: XMLHttpRequest, ...openArgs: unknown[]): void {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const xhr = this;\n const method = openArgs[0] as string;\n const headers: Record<string, string> = {};\n\n // Intercepting `setRequestHeader` to access the request headers of XHR instance.\n // This will only work for user/library defined headers, not for the default/browser-assigned headers.\n // Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.\n fill(\n xhr,\n 'setRequestHeader',\n // eslint-disable-next-line @typescript-eslint/ban-types\n function (originalSetRequestHeader: (...setRequestHeaderArgs: unknown[]) => void): Function {\n return function (...setRequestHeaderArgs: unknown[]): void {\n const [header, value] = setRequestHeaderArgs as [string, string];\n\n headers[header] = value;\n\n return originalSetRequestHeader.apply(xhr, setRequestHeaderArgs);\n };\n },\n );\n\n // eslint-disable-next-line @typescript-eslint/ban-types\n fill(xhr, 'onloadend', function (original?: (...onloadendArgs: unknown[]) => void): Function {\n return function (...onloadendArgs: unknown[]): void {\n try {\n self._xhrResponseHandler(xhr, method, headers);\n } catch (e) {\n __DEBUG_BUILD__ && logger.warn('Error while extracting response event form XHR response', e);\n }\n\n if (original) {\n return original.apply(xhr, onloadendArgs);\n }\n };\n });\n\n return originalOpen.apply(this, openArgs);\n };\n });\n }\n\n /**\n * Checks whether given url points to Sentry server\n *\n * @param url url to verify\n */\n private _isSentryRequest(url: string): boolean {\n const client = this._getCurrentHub && this._getCurrentHub().getClient();\n\n if (!client) {\n return false;\n }\n\n const dsn = client.getDsn();\n return dsn ? url.includes(dsn.host) : false;\n }\n\n /**\n * Checks whether to capture given response as an event\n *\n * @param status response status code\n * @param url response url\n */\n private _shouldCaptureResponse(status: number, url: string): boolean {\n return this._isInGivenStatusRanges(status) && this._isInGivenRequestTargets(url) && !this._isSentryRequest(url);\n }\n\n /**\n * Creates a synthetic Sentry event from given response data\n *\n * @param data response data\n * @returns event\n */\n private _createEvent(data: {\n url: string;\n method: string;\n status: number;\n responseHeaders?: Record<string, string>;\n responseCookies?: Record<string, string>;\n requestHeaders?: Record<string, string>;\n requestCookies?: Record<string, string>;\n }): SentryEvent {\n const message = `HTTP Client Error with status code: ${data.status}`;\n\n const event: SentryEvent = {\n message,\n exception: {\n values: [\n {\n type: 'Error',\n value: message,\n },\n ],\n },\n request: {\n url: data.url,\n method: data.method,\n headers: data.requestHeaders,\n cookies: data.requestCookies,\n },\n contexts: {\n response: {\n status_code: data.status,\n headers: data.responseHeaders,\n cookies: data.responseCookies,\n body_size: this._getResponseSizeFromHeaders(data.responseHeaders),\n },\n },\n };\n\n addExceptionMechanism(event, {\n type: 'http.client',\n });\n\n return event;\n }\n}\n"],"names":[],"mappings":"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"}
\No newline at end of file