Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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.
*/
import { isDatadogCustomHeader } from '../headers';

describe('headers', () => {
describe('isDatadogCustomHeader', () => {
it('returns false for non-custom headers', () => {
expect(isDatadogCustomHeader('non-custom-header')).toBeFalsy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ export const getTracingHeadersFromAttributes = (
if (tracingAttributes.tracingStrategy === 'DISCARD') {
return headers;
}

let hasDatadogOrW3CPropagator = false;
tracingAttributes.propagatorTypes.forEach(propagator => {
switch (propagator) {
case PropagatorType.DATADOG: {
hasDatadogOrW3CPropagator = true;
headers.push(
{
header: ORIGIN_HEADER_KEY,
Expand Down Expand Up @@ -82,6 +85,7 @@ export const getTracingHeadersFromAttributes = (
break;
}
case PropagatorType.TRACECONTEXT: {
hasDatadogOrW3CPropagator = true;
const isSampled =
tracingAttributes.samplingPriorityHeader === '1';
headers.push(
Expand Down Expand Up @@ -137,14 +141,15 @@ export const getTracingHeadersFromAttributes = (
);
}
}
if (tracingAttributes.rumSessionId) {
headers.push({
header: BAGGAGE_HEADER_KEY,
value: `${DD_RUM_SESSION_ID_TAG}=${tracingAttributes.rumSessionId}`
});
}
});

if (hasDatadogOrW3CPropagator && tracingAttributes.rumSessionId) {
headers.push({
header: BAGGAGE_HEADER_KEY,
value: `${DD_RUM_SESSION_ID_TAG}=${tracingAttributes.rumSessionId}`
});
}

return headers;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
* Copyright 2016-Present Datadog, Inc.
*/

import { isDatadogCustomHeader } from '../../headers';
import {
DATADOG_GRAPH_QL_OPERATION_NAME_HEADER,
DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER,
DATADOG_GRAPH_QL_VARIABLES_HEADER,
isDatadogCustomHeader
DATADOG_GRAPH_QL_VARIABLES_HEADER
} from '../graphqlHeaders';

describe('GraphQL custom headers', () => {
Expand All @@ -19,10 +19,4 @@ describe('GraphQL custom headers', () => {
])('%s matches the custom header pattern', header => {
expect(isDatadogCustomHeader(header)).toBeTruthy();
});

describe('isDatadogCustomHeader', () => {
it('returns false for non-custom headers', () => {
expect(isDatadogCustomHeader('non-custom-header')).toBeFalsy();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { DATADOG_CUSTOM_HEADER_PREFIX } from '../headers';

/*
* 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 DATADOG_CUSTOM_HEADER_PREFIX = '_dd-custom-header';

export const DATADOG_GRAPH_QL_OPERATION_NAME_HEADER = `${DATADOG_CUSTOM_HEADER_PREFIX}-graph-ql-operation-name`;
export const DATADOG_GRAPH_QL_VARIABLES_HEADER = `${DATADOG_CUSTOM_HEADER_PREFIX}-graph-ql-variables`;
export const DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER = `${DATADOG_CUSTOM_HEADER_PREFIX}-graph-ql-operation-type`;

export const isDatadogCustomHeader = (header: string) => {
return header.match(new RegExp(`^${DATADOG_CUSTOM_HEADER_PREFIX}`));
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

export const DATADOG_CUSTOM_HEADER_PREFIX = '_dd-custom-header';
export const DATADOG_BAGGAGE_HEADER = `${DATADOG_CUSTOM_HEADER_PREFIX}-baggage`;

export const isDatadogCustomHeader = (header: string) => {
return header.match(new RegExp(`^${DATADOG_CUSTOM_HEADER_PREFIX}`));
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@

import { Timer } from '../../../../../utils/Timer';
import { getCachedSessionId } from '../../../../sessionId/sessionIdHelper';
import { getTracingHeadersFromAttributes } from '../../distributedTracing/distributedTracingHeaders';
import {
BAGGAGE_HEADER_KEY,
getTracingHeadersFromAttributes
} from '../../distributedTracing/distributedTracingHeaders';
import type { DdRumResourceTracingAttributes } from '../../distributedTracing/distributedTracing';
import { getTracingAttributes } from '../../distributedTracing/distributedTracing';
import {
DATADOG_GRAPH_QL_OPERATION_NAME_HEADER,
DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER,
DATADOG_GRAPH_QL_VARIABLES_HEADER,
isDatadogCustomHeader
DATADOG_GRAPH_QL_VARIABLES_HEADER
} from '../../graphql/graphqlHeaders';
import { DATADOG_BAGGAGE_HEADER, isDatadogCustomHeader } from '../../headers';
import type { RequestProxyOptions } from '../interfaces/RequestProxy';
import { RequestProxy } from '../interfaces/RequestProxy';

import type { ResourceReporter } from './DatadogRumResource/ResourceReporter';
import { URLHostParser } from './URLHostParser';
import { formatBaggageHeader } from './baggageHeaderUtils';
import { calculateResponseSize } from './responseSize';

const RESPONSE_START_LABEL = 'response_start';
Expand All @@ -39,6 +43,7 @@ interface DdRumXhrContext {
reported: boolean;
timer: Timer;
tracingAttributes: DdRumResourceTracingAttributes;
baggageHeaderEntries: Set<string>;
}

interface XHRProxyProviders {
Expand Down Expand Up @@ -111,7 +116,8 @@ const proxyOpen = (
firstPartyHostsRegexMap,
tracingSamplingRate,
rumSessionId: getCachedSessionId()
})
}),
baggageHeaderEntries: new Set<string>()
};
// eslint-disable-next-line prefer-rest-params
return originalXhrOpen.apply(this, arguments as any);
Expand All @@ -127,12 +133,22 @@ const proxySend = (providers: XHRProxyProviders): void => {
// keep track of start time
this._datadog_xhr.timer.start();

// Tracing Headers
const tracingHeaders = getTracingHeadersFromAttributes(
this._datadog_xhr.tracingAttributes
);

tracingHeaders.forEach(({ header, value }) => {
this.setRequestHeader(header, value);
});

// Join all baggage header entries
const baggageHeader = formatBaggageHeader(
this._datadog_xhr.baggageHeaderEntries
);
if (baggageHeader) {
this.setRequestHeader(DATADOG_BAGGAGE_HEADER, baggageHeader);
}
}

proxyOnReadyStateChange(this, providers);
Expand Down Expand Up @@ -211,22 +227,37 @@ const proxySetRequestHeader = (providers: XHRProxyProviders): void => {
header: string,
value: string
) {
if (isDatadogCustomHeader(header)) {
if (header === DATADOG_GRAPH_QL_OPERATION_NAME_HEADER) {
this._datadog_xhr.graphql.operationName = value;
return;
}
if (header === DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER) {
this._datadog_xhr.graphql.operationType = value;
return;
}
if (header === DATADOG_GRAPH_QL_VARIABLES_HEADER) {
this._datadog_xhr.graphql.variables = value;
return;
const key = header.toLowerCase();
if (isDatadogCustomHeader(key)) {
switch (key) {
case DATADOG_GRAPH_QL_OPERATION_NAME_HEADER:
this._datadog_xhr.graphql.operationName = value;
break;
case DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER:
this._datadog_xhr.graphql.operationType = value;
break;
case DATADOG_GRAPH_QL_VARIABLES_HEADER:
this._datadog_xhr.graphql.variables = value;
break;
case DATADOG_BAGGAGE_HEADER:
// Apply Baggage Header only if pre-processed by Datadog
return originalXhrSetRequestHeader.apply(this, [
BAGGAGE_HEADER_KEY,
value
]);
default:
return originalXhrSetRequestHeader.apply(
this,
// eslint-disable-next-line prefer-rest-params
arguments as any
);
}
} else if (key === BAGGAGE_HEADER_KEY) {
// Intercept User Baggage Header entries to apply them later
this._datadog_xhr.baggageHeaderEntries?.add(value);
} else {
// eslint-disable-next-line prefer-rest-params
return originalXhrSetRequestHeader.apply(this, arguments as any);
}

// eslint-disable-next-line prefer-rest-params
return originalXhrSetRequestHeader.apply(this, arguments as any);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,93 @@ describe('XHRProxy', () => {
// THEN
expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toBeUndefined();
});

it('does not add rum session id to baggage headers when propagator type is not datadog or w3c', async () => {
// GIVEN
const method = 'GET';
const url = 'https://example.com';
xhrProxy.onTrackingStart({
tracingSamplingRate: 100,
firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([
{
match: 'api.example.com',
propagatorTypes: [
PropagatorType.DATADOG,
PropagatorType.TRACECONTEXT
]
},
{
match: 'example.com', // <-- no datadog or tracecontext here
propagatorTypes: [
PropagatorType.B3,
PropagatorType.B3MULTI
]
}
])
});

setCachedSessionId('TEST-SESSION-ID');

// WHEN
const xhr = new XMLHttpRequestMock();
xhr.open(method, url);
xhr.send();
xhr.notifyResponseArrived();
xhr.complete(200, 'ok');
await flushPromises();

// THEN
expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toBeUndefined();
});

it('rum session id does not overwrite existing baggage headers', async () => {
// GIVEN
const method = 'GET';
const url = 'https://api.example.com:443/v2/user';
xhrProxy.onTrackingStart({
tracingSamplingRate: 100,
firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([
{
match: 'api.example.com',
propagatorTypes: [
PropagatorType.DATADOG,
PropagatorType.TRACECONTEXT
]
},
{
match: 'example.com',
propagatorTypes: [
PropagatorType.B3,
PropagatorType.B3MULTI
]
}
])
});

setCachedSessionId('TEST-SESSION-ID');

// WHEN
const xhr = new XMLHttpRequestMock();
xhr.open(method, url);
xhr.setRequestHeader('baggage', 'existing.key=existing-value');
xhr.send();
xhr.notifyResponseArrived();
xhr.complete(200, 'ok');
await flushPromises();

// THEN
expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).not.toBeUndefined();
expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toContain(
'existing.key=existing-value'
);

const values = xhr.requestHeaders[BAGGAGE_HEADER_KEY].split(
','
).sort();

expect(values[0]).toBe('existing.key=existing-value');
expect(values[1]).toBe('session.id=TEST-SESSION-ID');
});
});

describe('DdRum.startResource calls', () => {
Expand Down
Loading
Loading