diff --git a/e2e-cli/e2e-config.json b/e2e-cli/e2e-config.json index ac88545d2..a7417576d 100644 --- a/e2e-cli/e2e-config.json +++ b/e2e-cli/e2e-config.json @@ -1,9 +1,10 @@ { "sdk": "react-native", - "test_suites": "basic,settings,retry", + "test_suites": "basic,settings,retry,retry-settings", "auto_settings": true, "patch": null, "env": { - "BROWSER_BATCHING": "true" + "BROWSER_BATCHING": "true", + "HTTP_CONFIG_SETTINGS": "true" } } diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts index c55fab691..f661778ea 100644 --- a/packages/core/src/errors.ts +++ b/packages/core/src/errors.ts @@ -147,11 +147,19 @@ export const classifyError = ( default5xxBehavior?: 'drop' | 'retry'; statusCodeOverrides?: Record; rateLimitEnabled?: boolean; + backoffEnabled?: boolean; } ): ErrorClassification => { const override = config?.statusCodeOverrides?.[statusCode.toString()]; if (override !== undefined) { if (override === 'retry') { + // If the relevant config is disabled, treat retry overrides as permanent + if (statusCode === 429 && config?.rateLimitEnabled === false) { + return new ErrorClassification('permanent'); + } + if (statusCode !== 429 && config?.backoffEnabled === false) { + return new ErrorClassification('permanent'); + } return statusCode === 429 ? new ErrorClassification('rate_limit') : new ErrorClassification('transient'); @@ -159,12 +167,17 @@ export const classifyError = ( return new ErrorClassification('permanent'); } - if (statusCode === 429 && config?.rateLimitEnabled !== false) { - return new ErrorClassification('rate_limit'); + if (statusCode === 429) { + return config?.rateLimitEnabled !== false + ? new ErrorClassification('rate_limit') + : new ErrorClassification('permanent'); } if (statusCode >= 400 && statusCode < 500) { const behavior = config?.default4xxBehavior ?? 'drop'; + if (behavior === 'retry' && config?.backoffEnabled === false) { + return new ErrorClassification('permanent'); + } return new ErrorClassification( behavior === 'retry' ? 'transient' : 'permanent' ); @@ -172,6 +185,9 @@ export const classifyError = ( if (statusCode >= 500 && statusCode < 600) { const behavior = config?.default5xxBehavior ?? 'retry'; + if (behavior === 'retry' && config?.backoffEnabled === false) { + return new ErrorClassification('permanent'); + } return new ErrorClassification( behavior === 'retry' ? 'transient' : 'permanent' ); diff --git a/packages/core/src/plugins/SegmentDestination.ts b/packages/core/src/plugins/SegmentDestination.ts index b802c0d04..b291c8b9d 100644 --- a/packages/core/src/plugins/SegmentDestination.ts +++ b/packages/core/src/plugins/SegmentDestination.ts @@ -25,6 +25,7 @@ import { } from '../errors'; import { RetryManager } from '../backoff/RetryManager'; import type { RetryResult } from '../backoff'; +import { extractHttpConfig } from '../config-validation'; const MAX_EVENTS_PER_BATCH = 100; const MAX_PAYLOAD_SIZE_IN_KB = 500; @@ -86,6 +87,7 @@ export class SegmentDestination extends DestinationPlugin { default5xxBehavior: this.getBackoffConfig()?.default5xxBehavior, statusCodeOverrides: this.getBackoffConfig()?.statusCodeOverrides, rateLimitEnabled: this.getRateLimitConfig()?.enabled, + backoffEnabled: this.getBackoffConfig()?.enabled, }); switch (classification.errorType) { @@ -419,7 +421,22 @@ export class SegmentDestination extends DestinationPlugin { this.apiHost = `https://${segmentSettings.apiHost}/b`; } - const httpConfig = this.analytics?.getHttpConfig(); + // Read httpConfig: prefer integration-level settings from CDN, fall back to + // top-level CDN config merged with client config (via analytics.getHttpConfig()). + const rawIntegration = settings.integrations[this.key] as + | Record + | undefined; + let httpConfig: HttpConfig | undefined; + if (rawIntegration?.httpConfig !== undefined) { + httpConfig = extractHttpConfig( + rawIntegration.httpConfig as HttpConfig, + this.analytics?.logger + ); + } + if (!httpConfig) { + httpConfig = this.analytics?.getHttpConfig(); + } + if (httpConfig) { this.httpConfig = httpConfig;