@datadog/mobile-react-native
Version:
A client-side React Native module to interact with Datadog
163 lines (159 loc) • 5.98 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.XHRProxy = void 0;
var _Timer = require("../../../../../utils/Timer");
var _sessionIdHelper = require("../../../../sessionId/sessionIdHelper");
var _distributedTracingHeaders = require("../../distributedTracing/distributedTracingHeaders");
var _distributedTracing = require("../../distributedTracing/distributedTracing");
var _graphqlHeaders = require("../../graphql/graphqlHeaders");
var _RequestProxy = require("../interfaces/RequestProxy");
var _URLHostParser = require("./URLHostParser");
var _responseSize = require("./responseSize");
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/
const RESPONSE_START_LABEL = 'response_start';
/**
* Proxies XMLHttpRequest to track resources.
*/
class XHRProxy extends _RequestProxy.RequestProxy {
constructor(providers) {
super();
this.providers = providers;
}
onTrackingStart = context => {
XHRProxy.originalXhrOpen = this.providers.xhrType.prototype.open;
XHRProxy.originalXhrSend = this.providers.xhrType.prototype.send;
XHRProxy.originalXhrSetRequestHeader = this.providers.xhrType.prototype.setRequestHeader;
proxyRequests(this.providers, context);
};
onTrackingStop = () => {
this.providers.xhrType.prototype.open = XHRProxy.originalXhrOpen;
this.providers.xhrType.prototype.send = XHRProxy.originalXhrSend;
this.providers.xhrType.prototype.setRequestHeader = XHRProxy.originalXhrSetRequestHeader;
};
}
exports.XHRProxy = XHRProxy;
const proxyRequests = (providers, context) => {
proxyOpen(providers, context);
proxySend(providers);
proxySetRequestHeader(providers);
};
const proxyOpen = ({
xhrType
}, context) => {
const originalXhrOpen = xhrType.prototype.open;
const firstPartyHostsRegexMap = context.firstPartyHostsRegexMap;
const tracingSamplingRate = context.tracingSamplingRate;
xhrType.prototype.open = function open(method, url) {
const hostname = (0, _URLHostParser.URLHostParser)(url);
// Keep track of the method and url
// start time is tracked by the `send` method
this._datadog_xhr = {
method,
url,
reported: false,
timer: new _Timer.Timer(),
graphql: {},
tracingAttributes: (0, _distributedTracing.getTracingAttributes)({
hostname,
firstPartyHostsRegexMap,
tracingSamplingRate,
rumSessionId: (0, _sessionIdHelper.getCachedSessionId)()
})
};
// eslint-disable-next-line prefer-rest-params
return originalXhrOpen.apply(this, arguments);
};
};
const proxySend = providers => {
const xhrType = providers.xhrType;
const originalXhrSend = xhrType.prototype.send;
xhrType.prototype.send = function send() {
if (this._datadog_xhr) {
// keep track of start time
this._datadog_xhr.timer.start();
const tracingHeaders = (0, _distributedTracingHeaders.getTracingHeadersFromAttributes)(this._datadog_xhr.tracingAttributes);
tracingHeaders.forEach(({
header,
value
}) => {
this.setRequestHeader(header, value);
});
}
proxyOnReadyStateChange(this, providers);
// eslint-disable-next-line prefer-rest-params
return originalXhrSend.apply(this, arguments);
};
};
const proxyOnReadyStateChange = (xhrProxy, providers) => {
const xhrType = providers.xhrType;
const originalOnreadystatechange = xhrProxy.onreadystatechange;
xhrProxy.onreadystatechange = function onreadystatechange() {
if (xhrProxy.readyState === xhrType.DONE) {
if (!xhrProxy._datadog_xhr.reported) {
reportXhr(xhrProxy, providers.resourceReporter);
xhrProxy._datadog_xhr.reported = true;
}
} else if (xhrProxy.readyState === xhrType.HEADERS_RECEIVED) {
xhrProxy._datadog_xhr.timer.recordTick(RESPONSE_START_LABEL);
}
if (originalOnreadystatechange) {
// eslint-disable-next-line prefer-rest-params
originalOnreadystatechange.apply(xhrProxy, arguments);
}
};
};
const reportXhr = async (xhrProxy, resourceReporter) => {
const responseSize = (0, _responseSize.calculateResponseSize)(xhrProxy);
const context = xhrProxy._datadog_xhr;
const key = `${context.timer.startTime}/${context.method}`;
context.timer.stop();
resourceReporter.reportResource({
key,
request: {
method: context.method,
url: context.url,
kind: 'xhr'
},
graphqlAttributes: context.graphql,
tracingAttributes: context.tracingAttributes,
response: {
statusCode: xhrProxy.status,
size: responseSize
},
timings: {
startTime: context.timer.startTime,
stopTime: context.timer.stopTime,
responseStartTime: context.timer.hasTickFor(RESPONSE_START_LABEL) ? context.timer.timeAt(RESPONSE_START_LABEL) : undefined
},
resourceContext: xhrProxy
});
};
const proxySetRequestHeader = providers => {
const xhrType = providers.xhrType;
const originalXhrSetRequestHeader = xhrType.prototype.setRequestHeader;
xhrType.prototype.setRequestHeader = function sendRequestHeader(header, value) {
if ((0, _graphqlHeaders.isDatadogCustomHeader)(header)) {
if (header === _graphqlHeaders.DATADOG_GRAPH_QL_OPERATION_NAME_HEADER) {
this._datadog_xhr.graphql.operationName = value;
return;
}
if (header === _graphqlHeaders.DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER) {
this._datadog_xhr.graphql.operationType = value;
return;
}
if (header === _graphqlHeaders.DATADOG_GRAPH_QL_VARIABLES_HEADER) {
this._datadog_xhr.graphql.variables = value;
return;
}
}
// eslint-disable-next-line prefer-rest-params
return originalXhrSetRequestHeader.apply(this, arguments);
};
};
//# sourceMappingURL=XHRProxy.js.map