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
4 changes: 2 additions & 2 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ module.exports = [
name: 'CDN Bundle',
path: createCDNPath('bundle.min.js'),
gzip: true,
limit: '27 KB',
limit: '27.5 KB',
},
{
name: 'CDN Bundle (incl. Tracing)',
Expand Down Expand Up @@ -183,7 +183,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.min.js'),
gzip: false,
brotli: false,
limit: '123 KB',
limit: '124 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, test } from '@playwright/test';
import { waitForError } from '@sentry-internal/test-utils';
import { waitForError, waitForRequest } from '@sentry-internal/test-utils';
import { SDK_VERSION } from '@sentry/cloudflare';
import { WebSocket } from 'ws';

test('Index page', async ({ baseURL }) => {
Expand Down Expand Up @@ -69,3 +70,15 @@ test('Websocket.webSocketClose', async ({ baseURL }) => {
expect(event.exception?.values?.[0]?.value).toBe('Should be recorded in Sentry: webSocketClose');
expect(event.exception?.values?.[0]?.mechanism?.type).toBe('auto.faas.cloudflare.durable_object');
});

test('sends user-agent header with SDK name and version in envelope requests', async ({ baseURL }) => {
const requestPromise = waitForRequest('cloudflare-workers', () => true);

await fetch(`${baseURL}/throwException`);

const request = await requestPromise;

expect(request.rawProxyRequestHeaders).toMatchObject({
'user-agent': `sentry.javascript.cloudflare/${SDK_VERSION}`,
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect, test } from '@playwright/test';
import { waitForRequest } from '@sentry-internal/test-utils';
import { SDK_VERSION } from '@sentry/node';

test('sends user-agent header with SDK name and version in envelope requests', async ({ baseURL }) => {
const requestPromise = waitForRequest('node-express', () => true);

await fetch(`${baseURL}/test-exception/123`);

const request = await requestPromise;

expect(request.rawProxyRequestHeaders).toMatchObject({
'user-agent': `sentry.javascript.node/${SDK_VERSION}`,
});
});
2 changes: 2 additions & 0 deletions dev-packages/test-utils/src/event-proxy-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface EventProxyServerOptions {
interface SentryRequestCallbackData {
envelope: Envelope;
rawProxyRequestBody: string;
rawProxyRequestHeaders: Record<string, string | string[] | undefined>;
rawSentryResponseBody: string;
sentryResponseStatusCode?: number;
}
Expand Down Expand Up @@ -182,6 +183,7 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P
const data: SentryRequestCallbackData = {
envelope: parseEnvelope(proxyRequestBody),
rawProxyRequestBody: proxyRequestBody,
rawProxyRequestHeaders: proxyRequest.headers,
rawSentryResponseBody: '',
sentryResponseStatusCode: 200,
};
Expand Down
2 changes: 0 additions & 2 deletions packages/browser/src/transports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@ import type { BaseTransportOptions } from '@sentry/core';
export interface BrowserTransportOptions extends BaseTransportOptions {
/** Fetch API init parameters. Used by the FetchTransport */
fetchOptions?: RequestInit;
/** Custom headers for the transport. Used by the XHRTransport and FetchTransport */
headers?: { [key: string]: string };
}
7 changes: 1 addition & 6 deletions packages/bun/src/transports/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core';
import { createTransport, suppressTracing } from '@sentry/core';

export interface BunTransportOptions extends BaseTransportOptions {
/** Custom headers for the transport. Used by the XHRTransport and FetchTransport */
headers?: { [key: string]: string };
}

/**
* Creates a Transport that uses the Fetch API to send events to Sentry.
*/
export function makeFetchTransport(options: BunTransportOptions): Transport {
export function makeFetchTransport(options: BaseTransportOptions): Transport {
function makeRequest(request: TransportRequest): PromiseLike<TransportMakeRequestResponse> {
const requestOptions: RequestInit = {
body: request.body,
Expand Down
7 changes: 3 additions & 4 deletions packages/bun/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ClientOptions, Options, TracePropagationTargets } from '@sentry/core';
import type { BunTransportOptions } from './transports';
import type { BaseTransportOptions, ClientOptions, Options, TracePropagationTargets } from '@sentry/core';

export interface BaseBunOptions {
/**
Expand Down Expand Up @@ -43,10 +42,10 @@ export interface BaseBunOptions {
* Configuration options for the Sentry Bun SDK
* @see @sentry/core Options for more information.
*/
export interface BunOptions extends Options<BunTransportOptions>, BaseBunOptions {}
export interface BunOptions extends Options<BaseTransportOptions>, BaseBunOptions {}

/**
* Configuration options for the Sentry Bun SDK Client class
* @see BunClient for more information.
*/
export interface BunClientOptions extends ClientOptions<BunTransportOptions>, BaseBunOptions {}
export interface BunClientOptions extends ClientOptions<BaseTransportOptions>, BaseBunOptions {}
2 changes: 0 additions & 2 deletions packages/cloudflare/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { createTransport, SENTRY_BUFFER_FULL_ERROR, suppressTracing } from '@sen
export interface CloudflareTransportOptions extends BaseTransportOptions {
/** Fetch API init parameters. */
fetchOptions?: RequestInit;
/** Custom headers for the transport. */
headers?: { [key: string]: string };
}

const DEFAULT_TRANSPORT_BUFFER_SIZE = 30;
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/server-runtime-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getIsolationScope } from './currentScopes';
import { DEBUG_BUILD } from './debug-build';
import type { Scope } from './scope';
import { registerSpanErrorInstrumentation } from './tracing';
import { addUserAgentToTransportHeaders } from './transports/userAgent';
import type { CheckIn, MonitorConfig, SerializedCheckIn } from './types-hoist/checkin';
import type { Event, EventHint } from './types-hoist/event';
import type { ClientOptions } from './types-hoist/options';
Expand Down Expand Up @@ -36,6 +37,8 @@ export class ServerRuntimeClient<
// Server clients always support tracing
registerSpanErrorInstrumentation();

addUserAgentToTransportHeaders(options);

super(options);
}

Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/transports/userAgent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { ClientOptions } from '../types-hoist/options';

/**
* Takes the SDK metadata and adds the user-agent header to the transport options.
* This ensures that the SDK sends the user-agent header with SDK name and version to
* all requests made by the transport.
*
* @see https://develop.sentry.dev/sdk/overview/#user-agent
*/
export function addUserAgentToTransportHeaders(options: ClientOptions): void {
const sdkMetadata = options._metadata?.sdk;
const sdkUserAgent =
sdkMetadata?.name && sdkMetadata?.version ? `${sdkMetadata?.name}/${sdkMetadata?.version}` : undefined;

options.transportOptions = {
...options.transportOptions,
headers: {
...(sdkUserAgent && { 'user-agent': sdkUserAgent }),
...options.transportOptions?.headers,
},
};
}
5 changes: 5 additions & 0 deletions packages/core/src/types-hoist/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export interface BaseTransportOptions extends InternalBaseTransportOptions {
// transport does not care about dsn specific - client should take care of
// parsing and figuring that out
url: string;

/**
* Custom HTTP headers to be added to requests made by the transport.
*/
headers?: { [key: string]: string };
}

export interface Transport {
Expand Down
33 changes: 32 additions & 1 deletion packages/core/test/lib/server-runtime-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it, test, vi } from 'vitest';
import { createTransport, Scope } from '../../src';
import { applySdkMetadata, createTransport, Scope } from '../../src';
import type { ServerRuntimeClientOptions } from '../../src/server-runtime-client';
import { ServerRuntimeClient } from '../../src/server-runtime-client';
import type { Event, EventHint } from '../../src/types-hoist/event';
Expand Down Expand Up @@ -205,4 +205,35 @@ describe('ServerRuntimeClient', () => {
]);
});
});

describe('user-agent header', () => {
it('sends user-agent header with SDK name and version', () => {
const options = getDefaultClientOptions({ dsn: PUBLIC_DSN });

// this is done in all `init` functions of the respective SDKs:
applySdkMetadata(options, 'core');

client = new ServerRuntimeClient(options);

expect(client.getOptions().transportOptions?.headers).toEqual({
'user-agent': 'sentry.javascript.core/0.0.0-unknown.0',
});
});

it('prefers user-passed headers (including user-agent)', () => {
const options = getDefaultClientOptions({
dsn: PUBLIC_DSN,
transportOptions: { headers: { 'x-custom-header': 'custom-value', 'user-agent': 'custom-user-agent' } },
});

applySdkMetadata(options, 'core');

client = new ServerRuntimeClient(options);

expect(client.getOptions().transportOptions?.headers).toEqual({
'user-agent': 'custom-user-agent',
'x-custom-header': 'custom-value',
});
});
});
});
7 changes: 1 addition & 6 deletions packages/deno/src/transports/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core';
import { consoleSandbox, createTransport, debug, suppressTracing } from '@sentry/core';

export interface DenoTransportOptions extends BaseTransportOptions {
/** Custom headers for the transport. Used by the XHRTransport and FetchTransport */
headers?: { [key: string]: string };
}

/**
* Creates a Transport that uses the Fetch API to send events to Sentry.
*/
export function makeFetchTransport(options: DenoTransportOptions): Transport {
export function makeFetchTransport(options: BaseTransportOptions): Transport {
const url = new URL(options.url);

Deno.permissions
Expand Down
7 changes: 3 additions & 4 deletions packages/deno/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ClientOptions, Options, TracePropagationTargets } from '@sentry/core';
import type { DenoTransportOptions } from './transports';
import type { BaseTransportOptions, ClientOptions, Options, TracePropagationTargets } from '@sentry/core';

export interface BaseDenoOptions {
/**
Expand Down Expand Up @@ -44,10 +43,10 @@ export interface BaseDenoOptions {
* Configuration options for the Sentry Deno SDK
* @see @sentry/core Options for more information.
*/
export interface DenoOptions extends Options<DenoTransportOptions>, BaseDenoOptions {}
export interface DenoOptions extends Options<BaseTransportOptions>, BaseDenoOptions {}

/**
* Configuration options for the Sentry Deno SDK Client class
* @see DenoClient for more information.
*/
export interface DenoClientOptions extends ClientOptions<DenoTransportOptions>, BaseDenoOptions {}
export interface DenoClientOptions extends ClientOptions<BaseTransportOptions>, BaseDenoOptions {}
2 changes: 0 additions & 2 deletions packages/node-core/src/transports/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import { HttpsProxyAgent } from '../proxy';
import type { HTTPModule } from './http-module';

export interface NodeTransportOptions extends BaseTransportOptions {
/** Define custom headers */
headers?: Record<string, string>;
/** Set a proxy that should be used for outbound requests. */
proxy?: string;
/** HTTPS proxy CA certificates */
Expand Down
5 changes: 5 additions & 0 deletions packages/node-core/test/sdk/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ describe('NodeClient', () => {
dsn: expect.any(String),
integrations: [],
transport: options.transport,
transportOptions: {
headers: {
'user-agent': `sentry.javascript.node/${SDK_VERSION}`,
},
},
stackParser: options.stackParser,
_metadata: {
sdk: {
Expand Down
5 changes: 5 additions & 0 deletions packages/node/test/sdk/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ describe('NodeClient', () => {
dsn: expect.any(String),
integrations: [],
transport: options.transport,
transportOptions: {
headers: {
'user-agent': `sentry.javascript.node/${SDK_VERSION}`,
},
},
stackParser: options.stackParser,
_metadata: {
sdk: {
Expand Down
2 changes: 0 additions & 2 deletions packages/vercel-edge/src/transports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { createTransport, SENTRY_BUFFER_FULL_ERROR, suppressTracing } from '@sen
export interface VercelEdgeTransportOptions extends BaseTransportOptions {
/** Fetch API init parameters. */
fetchOptions?: RequestInit;
/** Custom headers for the transport. */
headers?: { [key: string]: string };
}

const DEFAULT_TRANSPORT_BUFFER_SIZE = 30;
Expand Down