dd-trace 5.87.0 → 5.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +60 -32
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +1 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +234 -4
- package/package.json +18 -11
- package/packages/datadog-instrumentations/src/ai.js +54 -90
- package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +27 -110
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/compiler.js +74 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +143 -17
- package/packages/datadog-instrumentations/src/jest.js +176 -54
- package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
- package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
- package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
- package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
- package/packages/datadog-plugin-cucumber/src/index.js +9 -6
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +62 -5
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
- package/packages/datadog-plugin-cypress/src/support.js +52 -9
- package/packages/datadog-plugin-jest/src/index.js +12 -2
- package/packages/datadog-plugin-jest/src/util.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
- package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
- package/packages/datadog-plugin-mocha/src/index.js +9 -6
- package/packages/datadog-plugin-playwright/src/index.js +10 -6
- package/packages/datadog-plugin-vitest/src/index.js +13 -8
- package/packages/dd-trace/src/aiguard/sdk.js +5 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
- package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
- package/packages/dd-trace/src/azure_metadata.js +0 -2
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +3 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +148 -197
- package/packages/dd-trace/src/config/helper.js +43 -1
- package/packages/dd-trace/src/config/index.js +38 -14
- package/packages/dd-trace/src/config/supported-configurations.json +4125 -512
- package/packages/dd-trace/src/constants.js +0 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
- package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
- package/packages/dd-trace/src/datastreams/index.js +3 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -0
- package/packages/dd-trace/src/datastreams/pathway.js +22 -3
- package/packages/dd-trace/src/datastreams/processor.js +140 -4
- package/packages/dd-trace/src/encode/agentless-json.js +155 -0
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/writer.js +21 -8
- package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
- package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
- package/packages/dd-trace/src/exporters/common/request.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
- package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
- package/packages/dd-trace/src/opentracing/span.js +6 -4
- package/packages/dd-trace/src/pkg.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
- package/packages/dd-trace/src/plugins/database.js +15 -2
- package/packages/dd-trace/src/plugins/util/test.js +48 -0
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
- package/packages/dd-trace/src/propagation-hash/index.js +145 -0
- package/packages/dd-trace/src/proxy.js +6 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/startup-log.js +53 -19
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/@datadog/source-map/index.js +1 -1
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
- package/vendor/dist/@opentelemetry/core/index.js +1 -1
- package/vendor/dist/@opentelemetry/resources/index.js +1 -1
- package/vendor/dist/astring/index.js +1 -1
- package/vendor/dist/crypto-randomuuid/index.js +1 -1
- package/vendor/dist/escape-string-regexp/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/ignore/index.js +1 -1
- package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
- package/vendor/dist/jest-docblock/index.js +1 -1
- package/vendor/dist/jsonpath-plus/index.js +1 -1
- package/vendor/dist/limiter/index.js +1 -1
- package/vendor/dist/lodash.sortby/index.js +1 -1
- package/vendor/dist/lru-cache/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/vendor/dist/module-details-from-path/index.js +1 -1
- package/vendor/dist/mutexify/promise/index.js +1 -1
- package/vendor/dist/opentracing/index.js +1 -1
- package/vendor/dist/path-to-regexp/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/retry/index.js +1 -1
- package/vendor/dist/rfdc/index.js +1 -1
- package/vendor/dist/semifies/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/vendor/dist/source-map/lib/util/index.js +1 -1
- package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
- package/vendor/dist/ttl-set/index.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +0 -33
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
- package/packages/dd-trace/src/scope/noop/scope.js +0 -21
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto')
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
const defaults = require('../../../config/defaults')
|
|
5
6
|
|
|
6
7
|
const STRINGIFY_RANGE_KEY = 'DD_' + crypto.randomBytes(20).toString('hex')
|
|
7
8
|
const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
|
|
@@ -11,7 +12,7 @@ const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
|
|
|
11
12
|
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(String.raw`(?:"(${STRINGIFY_RANGE_KEY}_\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s0-9.a-zA-Z]*)")`, 'gm')
|
|
12
13
|
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(String.raw`"(${STRINGIFY_RANGE_KEY}_\d+_)`, 'gm')
|
|
13
14
|
|
|
14
|
-
const sensitiveValueRegex = new RegExp(
|
|
15
|
+
const sensitiveValueRegex = new RegExp(/** @type {string} */ (defaults['iast.redactionValuePattern']), 'gmi')
|
|
15
16
|
|
|
16
17
|
function iterateObject (target, fn, levelKeys = [], depth = 10, visited = new Set()) {
|
|
17
18
|
for (const key of Object.keys(target)) {
|
|
@@ -58,7 +58,6 @@ function buildMetadata () {
|
|
|
58
58
|
WEBSITE_SITE_NAME,
|
|
59
59
|
} = getEnvironmentVariables()
|
|
60
60
|
|
|
61
|
-
const DD_AAS_DOTNET_EXTENSION_VERSION = getValueFromEnvSources('DD_AAS_DOTNET_EXTENSION_VERSION')
|
|
62
61
|
const DD_AZURE_RESOURCE_GROUP = getValueFromEnvSources('DD_AZURE_RESOURCE_GROUP')
|
|
63
62
|
|
|
64
63
|
const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
|
|
@@ -77,7 +76,6 @@ function buildMetadata () {
|
|
|
77
76
|
: (WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME))
|
|
78
77
|
|
|
79
78
|
return trimObject({
|
|
80
|
-
extensionVersion: DD_AAS_DOTNET_EXTENSION_VERSION,
|
|
81
79
|
functionRuntimeVersion: FUNCTIONS_EXTENSION_VERSION,
|
|
82
80
|
instanceID: WEBSITE_INSTANCE_ID,
|
|
83
81
|
instanceName: COMPUTERNAME,
|
|
@@ -206,6 +206,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
206
206
|
requireGit,
|
|
207
207
|
isEarlyFlakeDetectionEnabled,
|
|
208
208
|
earlyFlakeDetectionNumRetries,
|
|
209
|
+
earlyFlakeDetectionSlowTestRetries,
|
|
209
210
|
earlyFlakeDetectionFaultyThreshold,
|
|
210
211
|
isFlakyTestRetriesEnabled,
|
|
211
212
|
isDiEnabled,
|
|
@@ -222,6 +223,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
222
223
|
requireGit,
|
|
223
224
|
isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
|
|
224
225
|
earlyFlakeDetectionNumRetries,
|
|
226
|
+
earlyFlakeDetectionSlowTestRetries,
|
|
225
227
|
earlyFlakeDetectionFaultyThreshold,
|
|
226
228
|
isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && this._config.isFlakyTestRetriesEnabled,
|
|
227
229
|
flakyTestRetriesCount: this._config.flakyTestRetriesCount,
|
|
@@ -232,6 +234,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
232
234
|
testManagementAttemptToFixRetries ?? this._config.testManagementAttemptToFixRetries,
|
|
233
235
|
isImpactedTestsEnabled: isImpactedTestsEnabled && this._config.isImpactedTestsEnabled,
|
|
234
236
|
isCoverageReportUploadEnabled,
|
|
237
|
+
isKeepingCoverageConfiguration: this._config.isKeepingCoverageConfiguration,
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
240
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const request = require('../../exporters/common/request')
|
|
4
3
|
const id = require('../../id')
|
|
5
4
|
const log = require('../../log')
|
|
6
5
|
const { getValueFromEnvSources } = require('../../config/helper')
|
|
@@ -13,8 +12,10 @@ const {
|
|
|
13
12
|
TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE,
|
|
14
13
|
} = require('../telemetry')
|
|
15
14
|
const { writeSettingsToCache } = require('../test-optimization-cache')
|
|
15
|
+
const request = require('./request')
|
|
16
16
|
|
|
17
17
|
const DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES = 2
|
|
18
|
+
const DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES = { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
|
|
18
19
|
const DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD = 30
|
|
19
20
|
|
|
20
21
|
function getLibraryConfiguration ({
|
|
@@ -115,6 +116,8 @@ function getLibraryConfiguration ({
|
|
|
115
116
|
isEarlyFlakeDetectionEnabled: isKnownTestsEnabled && (earlyFlakeDetectionConfig?.enabled ?? false),
|
|
116
117
|
earlyFlakeDetectionNumRetries:
|
|
117
118
|
earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
|
|
119
|
+
earlyFlakeDetectionSlowTestRetries:
|
|
120
|
+
earlyFlakeDetectionConfig?.slow_test_retries ?? DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES,
|
|
118
121
|
earlyFlakeDetectionFaultyThreshold:
|
|
119
122
|
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
|
|
120
123
|
isFlakyTestRetriesEnabled,
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const http = require('http')
|
|
4
|
+
const https = require('https')
|
|
5
|
+
const zlib = require('zlib')
|
|
6
|
+
|
|
7
|
+
const { storage } = require('../../../../datadog-core')
|
|
8
|
+
const log = require('../../log')
|
|
9
|
+
const { httpAgent, httpsAgent } = require('../../exporters/common/agents')
|
|
10
|
+
const { urlToHttpOptions } = require('../../exporters/common/url-to-http-options-polyfill')
|
|
11
|
+
|
|
12
|
+
const RATE_LIMIT_MAX_WAIT_MS = 30_000
|
|
13
|
+
const RETRY_BASE_MS = 5000
|
|
14
|
+
const RETRY_JITTER_MS = 2500
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Calculates retry delay with jitter to prevent thundering herd.
|
|
18
|
+
* Delay is RETRY_BASE_MS + random(0, RETRY_JITTER_MS) (e.g. 5–7.5 seconds).
|
|
19
|
+
*
|
|
20
|
+
* @returns {number} Delay in milliseconds
|
|
21
|
+
*/
|
|
22
|
+
function getRetryDelay () {
|
|
23
|
+
return RETRY_BASE_MS + (Math.random() * RETRY_JITTER_MS)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Determines if a network error is retriable (transient failures only).
|
|
28
|
+
* ECONNREFUSED is retried because it can be transient (service starting up,
|
|
29
|
+
* restarts, rolling deploys, k8s pod/readiness transitions). ENOTFOUND is
|
|
30
|
+
* excluded as it indicates DNS failure or wrong host and is usually not transient.
|
|
31
|
+
*
|
|
32
|
+
* @param {Error} err - The error to check
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
function isRetriableNetworkError (err) {
|
|
36
|
+
if (!err.code) return false
|
|
37
|
+
return err.code === 'ECONNREFUSED' ||
|
|
38
|
+
err.code === 'ECONNRESET' ||
|
|
39
|
+
err.code === 'ETIMEDOUT' ||
|
|
40
|
+
err.code === 'EPIPE'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseUrl (urlObjOrString) {
|
|
44
|
+
if (urlObjOrString !== null && typeof urlObjOrString === 'object') {
|
|
45
|
+
return urlToHttpOptions(urlObjOrString)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const url = urlToHttpOptions(new URL(urlObjOrString))
|
|
49
|
+
|
|
50
|
+
if (url.protocol === 'unix:' && url.hostname === '.') {
|
|
51
|
+
const udsPath = urlObjOrString.slice(5)
|
|
52
|
+
url.path = udsPath
|
|
53
|
+
url.pathname = udsPath
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return url
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Simplified HTTP request for test optimization (library config). Uses common HTTP agents.
|
|
61
|
+
* Retries: 429 (with X-ratelimit-reset, max 30s wait),
|
|
62
|
+
* >=500 and transient network errors (5–7.5s delay with jitter). Max one retry.
|
|
63
|
+
* Destroys connections on errors to prevent reuse of bad connections. Preserves
|
|
64
|
+
* original status code across retries for telemetry.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} data - Request body (e.g. JSON string)
|
|
67
|
+
* @param {object} options - { url, path?, method?, headers?, timeout? } (may be mutated)
|
|
68
|
+
* @param {Function} callback - (err, res, statusCode) => void
|
|
69
|
+
*/
|
|
70
|
+
function request (data, options, callback) {
|
|
71
|
+
const headers = options.headers ? { ...options.headers } : {}
|
|
72
|
+
headers['Content-Length'] = Buffer.byteLength(data, 'utf8')
|
|
73
|
+
|
|
74
|
+
const opts = { ...options, method: 'POST', headers }
|
|
75
|
+
|
|
76
|
+
if (opts.url) {
|
|
77
|
+
const url = parseUrl(opts.url)
|
|
78
|
+
if (url.protocol === 'unix:') {
|
|
79
|
+
opts.socketPath = url.pathname
|
|
80
|
+
} else {
|
|
81
|
+
opts.path = opts.path ?? url.path
|
|
82
|
+
opts.protocol = url.protocol
|
|
83
|
+
opts.hostname = url.hostname
|
|
84
|
+
opts.port = url.port
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const timeout = opts.timeout || 2000
|
|
89
|
+
const isSecure = opts.protocol === 'https:'
|
|
90
|
+
const client = isSecure ? https : http
|
|
91
|
+
|
|
92
|
+
if (!opts.socketPath) {
|
|
93
|
+
opts.agent = isSecure ? httpsAgent : httpAgent
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let hasRetried = false
|
|
97
|
+
let firstStatusCode = null
|
|
98
|
+
|
|
99
|
+
const makeRequest = () => {
|
|
100
|
+
storage('legacy').run({ noop: true }, () => {
|
|
101
|
+
const req = client.request(opts, (res) => {
|
|
102
|
+
// Capture non-2xx status code as soon as we see it so telemetry preserves it if the retry
|
|
103
|
+
// fails with a network error (no HTTP response) before 'end' fires
|
|
104
|
+
if (res.statusCode >= 400 && firstStatusCode === null) {
|
|
105
|
+
firstStatusCode = res.statusCode
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const chunks = []
|
|
109
|
+
|
|
110
|
+
res.setTimeout(timeout)
|
|
111
|
+
|
|
112
|
+
res.on('data', chunk => {
|
|
113
|
+
chunks.push(chunk)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
res.once('end', () => {
|
|
117
|
+
const buffer = Buffer.concat(chunks)
|
|
118
|
+
|
|
119
|
+
if (res.statusCode >= 200 && res.statusCode <= 299) {
|
|
120
|
+
const isGzip = res.headers['content-encoding'] === 'gzip'
|
|
121
|
+
if (isGzip) {
|
|
122
|
+
zlib.gunzip(buffer, (err, result) => {
|
|
123
|
+
if (err) {
|
|
124
|
+
log.error('Could not gunzip response: %s', err.message)
|
|
125
|
+
callback(null, '', res.statusCode)
|
|
126
|
+
} else {
|
|
127
|
+
callback(null, result.toString(), res.statusCode)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
} else {
|
|
131
|
+
callback(null, buffer.toString(), res.statusCode)
|
|
132
|
+
}
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (res.statusCode === 429 && !hasRetried) {
|
|
137
|
+
const resetHeader = res.headers['x-ratelimit-reset']
|
|
138
|
+
const resetTs = (resetHeader === null || resetHeader === undefined)
|
|
139
|
+
? Number.NaN
|
|
140
|
+
: Number.parseInt(resetHeader, 10)
|
|
141
|
+
const waitMs = Number.isFinite(resetTs) ? Math.max(0, resetTs * 1000 - Date.now()) : Number.NaN
|
|
142
|
+
|
|
143
|
+
if (Number.isFinite(waitMs) && waitMs <= RATE_LIMIT_MAX_WAIT_MS) {
|
|
144
|
+
hasRetried = true
|
|
145
|
+
setTimeout(makeRequest, waitMs)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!Number.isFinite(waitMs) || waitMs > RATE_LIMIT_MAX_WAIT_MS) {
|
|
150
|
+
log.debug('Rate limited (429): drop payload (wait %sms > %sms or invalid header)',
|
|
151
|
+
Number.isFinite(waitMs) ? waitMs : 'N/A', RATE_LIMIT_MAX_WAIT_MS)
|
|
152
|
+
}
|
|
153
|
+
} else if (res.statusCode >= 500 && !hasRetried) {
|
|
154
|
+
try {
|
|
155
|
+
if (req.socket) req.socket.destroy()
|
|
156
|
+
} catch {
|
|
157
|
+
// ignore
|
|
158
|
+
}
|
|
159
|
+
hasRetried = true
|
|
160
|
+
setTimeout(makeRequest, getRetryDelay())
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const error = buildError(res, buffer, opts)
|
|
165
|
+
// Use original status code if this is a failed retry
|
|
166
|
+
callback(error, null, firstStatusCode === null ? res.statusCode : firstStatusCode)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
req.once('error', err => {
|
|
171
|
+
try {
|
|
172
|
+
if (req.socket) req.socket.destroy()
|
|
173
|
+
} catch {
|
|
174
|
+
// ignore
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Retry on retriable network errors
|
|
178
|
+
if (!hasRetried && isRetriableNetworkError(err)) {
|
|
179
|
+
hasRetried = true
|
|
180
|
+
setTimeout(makeRequest, getRetryDelay())
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Pass original status code (if any) for accurate telemetry
|
|
185
|
+
callback(err, null, firstStatusCode)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
req.setTimeout(timeout, () => {
|
|
189
|
+
try {
|
|
190
|
+
if (typeof req.abort === 'function') {
|
|
191
|
+
req.abort()
|
|
192
|
+
} else {
|
|
193
|
+
req.destroy()
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// ignore
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
req.write(data, 'utf8')
|
|
201
|
+
req.end()
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
makeRequest()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @param {object} res - IncomingMessage
|
|
210
|
+
* @param {Buffer} buffer - Response body
|
|
211
|
+
* @param {object} options - Request options
|
|
212
|
+
* @returns {Error}
|
|
213
|
+
*/
|
|
214
|
+
function buildError (res, buffer, options) {
|
|
215
|
+
let errorMessage = ''
|
|
216
|
+
try {
|
|
217
|
+
const fullUrl = new URL(
|
|
218
|
+
options.path,
|
|
219
|
+
options.url || options.hostname || `http://localhost:${options.port}`
|
|
220
|
+
).href
|
|
221
|
+
errorMessage = `Error from ${fullUrl}: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}.`
|
|
222
|
+
} catch {
|
|
223
|
+
// ignore
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const responseData = buffer.toString()
|
|
227
|
+
if (responseData) {
|
|
228
|
+
errorMessage += ` Response from the endpoint: "${responseData}"`
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const error = new log.NoTransmitError(errorMessage)
|
|
232
|
+
error.status = res.statusCode
|
|
233
|
+
return error
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = request
|