UNPKG

instabug-reactnative

Version:

React Native plugin for integrating the Instabug SDK

209 lines (208 loc) 8.61 kB
import InstabugConstants from '../utils/InstabugConstants'; import xhr from '../utils/XhrNetworkInterceptor'; import { InstabugRNConfig } from '../utils/config'; import { Logger } from '../utils/logger'; import { NativeInstabug } from '../native/NativeInstabug'; import { isContentTypeNotAllowed, registerFilteringAndObfuscationListener, registerFilteringListener, registerObfuscationListener, reportNetworkLog, } from '../utils/InstabugUtils'; import { NativeNetworkLogger, NativeNetworkLoggerEvent, NetworkListenerType, NetworkLoggerEmitter, } from '../native/NativeNetworkLogger'; import { Platform } from 'react-native'; let _networkDataObfuscationHandler; let _requestFilterExpression = 'false'; let _isNativeInterceptionEnabled = false; let _networkListener = null; let hasFilterExpression = false; function getPortFromUrl(url) { const portMatch = url.match(/:(\d+)(?=\/|$)/); return portMatch ? portMatch[1] : null; } /** * Sets whether network logs should be sent with bug reports. * It is enabled by default. * @param isEnabled */ export const setEnabled = (isEnabled) => { if (isEnabled) { xhr.enableInterception(); xhr.setOnDoneCallback(async (network) => { // eslint-disable-next-line no-new-func const predicate = Function('network', 'return ' + _requestFilterExpression); if (!predicate(network)) { const MAX_NETWORK_BODY_SIZE_IN_BYTES = await NativeInstabug.getNetworkBodyMaxSize(); try { if (_networkDataObfuscationHandler) { network = await _networkDataObfuscationHandler(network); } if (__DEV__) { const urlPort = getPortFromUrl(network.url); if (urlPort === InstabugRNConfig.metroDevServerPort) { return; } } if (network.requestBodySize > MAX_NETWORK_BODY_SIZE_IN_BYTES) { network.requestBody = `${InstabugConstants.MAX_REQUEST_BODY_SIZE_EXCEEDED_MESSAGE}${MAX_NETWORK_BODY_SIZE_IN_BYTES / 1024} Kb`; Logger.warn('IBG-RN:', `${InstabugConstants.MAX_REQUEST_BODY_SIZE_EXCEEDED_MESSAGE}${MAX_NETWORK_BODY_SIZE_IN_BYTES / 1024} Kb`); } if (network.responseBodySize > MAX_NETWORK_BODY_SIZE_IN_BYTES) { network.responseBody = `${InstabugConstants.MAX_RESPONSE_BODY_SIZE_EXCEEDED_MESSAGE}${MAX_NETWORK_BODY_SIZE_IN_BYTES / 1024} Kb`; Logger.warn('IBG-RN:', `${InstabugConstants.MAX_RESPONSE_BODY_SIZE_EXCEEDED_MESSAGE}${MAX_NETWORK_BODY_SIZE_IN_BYTES / 1024} Kb`); } if (network.requestBody && isContentTypeNotAllowed(network.requestContentType)) { network.requestBody = `Body is omitted because content type ${network.requestContentType} isn't supported`; Logger.warn(`IBG-RN: The request body for the network request with URL ${network.url} has been omitted because the content type ${network.requestContentType} isn't supported.`); } if (network.responseBody && isContentTypeNotAllowed(network.contentType)) { network.responseBody = `Body is omitted because content type ${network.contentType} isn't supported`; Logger.warn(`IBG-RN: The response body for the network request with URL ${network.url} has been omitted because the content type ${network.contentType} isn't supported.`); } reportNetworkLog(network); } catch (e) { Logger.error(e); } } }); } else { xhr.disableInterception(); } }; /** * @internal * Sets whether enabling or disabling native network interception. * It is disabled by default. * @param isEnabled */ export const setNativeInterceptionEnabled = (isEnabled) => { _isNativeInterceptionEnabled = isEnabled; }; export const getNetworkDataObfuscationHandler = () => _networkDataObfuscationHandler; export const getRequestFilterExpression = () => _requestFilterExpression; export const hasRequestFilterExpression = () => hasFilterExpression; /** * Obfuscates any response data. * @param handler */ export const setNetworkDataObfuscationHandler = (handler) => { _networkDataObfuscationHandler = handler; if (_isNativeInterceptionEnabled && Platform.OS === 'ios') { if (hasFilterExpression) { registerFilteringAndObfuscationListener(_requestFilterExpression); } else { registerObfuscationListener(); } } }; /** * Omit requests from being logged based on either their request or response details * @param expression */ export const setRequestFilterExpression = (expression) => { _requestFilterExpression = expression; hasFilterExpression = true; if (_isNativeInterceptionEnabled && Platform.OS === 'ios') { if (_networkDataObfuscationHandler) { registerFilteringAndObfuscationListener(_requestFilterExpression); } else { registerFilteringListener(_requestFilterExpression); } } }; /** * Returns progress in terms of totalBytesSent and totalBytesExpectedToSend a network request. * @param handler */ export const setProgressHandlerForRequest = (handler) => { xhr.setOnProgressCallback(handler); }; export const apolloLinkRequestHandler = (operation, forward) => { try { operation.setContext((context) => { const newHeaders = context.headers ?? {}; newHeaders[InstabugConstants.GRAPHQL_HEADER] = operation.operationName; return { headers: newHeaders }; }); } catch (e) { Logger.error(e); } return forward(operation); }; /** * Sets whether network body logs will be captured or not. * @param isEnabled */ export const setNetworkLogBodyEnabled = (isEnabled) => { NativeInstabug.setNetworkLogBodyEnabled(isEnabled); }; /** * @internal * Exported for internal/testing purposes only. */ export const resetNetworkListener = () => { if (process.env.NODE_ENV === 'test') { _networkListener = null; NativeNetworkLogger.resetNetworkLogsListener(); } else { Logger.error(`${InstabugConstants.IBG_APM_TAG}: The \`resetNetworkListener()\` method is intended solely for testing purposes.`); } }; /** * @internal * Exported for internal/testing purposes only. */ export const registerNetworkLogsListener = (type, handler) => { if (Platform.OS === 'ios') { // remove old listeners if (NetworkLoggerEmitter.listenerCount(NativeNetworkLoggerEvent.NETWORK_LOGGER_HANDLER) > 0) { NetworkLoggerEmitter.removeAllListeners(NativeNetworkLoggerEvent.NETWORK_LOGGER_HANDLER); } if (_networkListener == null) { // set new listener. _networkListener = type; } else { // attach a new listener to the existing one. _networkListener = NetworkListenerType.both; } } NetworkLoggerEmitter.addListener(NativeNetworkLoggerEvent.NETWORK_LOGGER_HANDLER, (networkSnapshot) => { // Mapping the data [Native -> React-Native]. const { id, url, requestHeader, requestBody, responseHeader, response, responseCode } = networkSnapshot; const networkSnapshotObj = { id: id, url: url, requestBody: requestBody, requestHeaders: requestHeader, method: '', responseBody: response, responseCode: responseCode, responseHeaders: responseHeader, contentType: '', duration: 0, requestBodySize: 0, responseBodySize: 0, errorDomain: '', errorCode: 0, startTime: 0, serverErrorMessage: '', requestContentType: '', isW3cHeaderFound: true, networkStartTimeInSeconds: 0, partialId: 0, w3cCaughtHeader: '', w3cGeneratedHeader: '', }; if (handler) { handler(networkSnapshotObj); } }); if (Platform.OS === 'ios') { NativeNetworkLogger.registerNetworkLogsListener(_networkListener ?? NetworkListenerType.both); } else { NativeNetworkLogger.registerNetworkLogsListener(); } };