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
2 changes: 1 addition & 1 deletion deps/undici/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const response = await fetch('https://api.example.com/data');
- Building isomorphic code that runs in browsers and Node.js
- Publishing to npm and want to maximize compatibility with JS runtimes
- Simple HTTP requests without advanced configuration
- You're publishing to npm and you want to maximize compatiblity
- You're publishing to npm and you want to maximize compatibility
- You don't depend on features from a specific version of undici

#### Use Undici Module When:
Expand Down
5 changes: 4 additions & 1 deletion deps/undici/src/docs/docs/api/Dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ added: v4.0.0
-->

* `options` {Object}
* `origin` {string|URL} (optional) The origin to dispatch the request to.
* `origin` {string|URL} The origin to dispatch the request to. Optional for a
dispatcher that is already bound to a single origin, such as `Client` or
`Pool`, which then use their own origin. Required on `Agent`, which is not
bound to one origin and throws an `InvalidArgumentError` when it is missing.
* `path` {string} The request path.
* `method` {string} The HTTP method, e.g. `'GET'` or `'POST'`.
* `body` {string|Buffer|Uint8Array|Readable|FormData|null} The request body.
Expand Down
2 changes: 1 addition & 1 deletion deps/undici/src/docs/docs/api/RetryAgent.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ added: v6.7.0
* `retryAfter` {boolean} When `true`, the delay between retries is inferred from
the `Retry-After` response header when present. **Default:** `true`.
* `methods` {string[]} HTTP methods that are eligible for retry.
**Default:** `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`.
**Default:** `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY']`.
* `statusCodes` {number[]} HTTP status codes that trigger a retry.
**Default:** `[500, 502, 503, 504, 429]`.
* `errorCodes` {string[]} Transport error codes that trigger a retry.
Expand Down
2 changes: 1 addition & 1 deletion deps/undici/src/docs/docs/api/RetryHandler.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ changes:
* `retryAfter` {boolean} When `true`, the delay before the next retry is inferred
from the `Retry-After` response header when present. **Default:** `true`.
* `methods` {string[]} HTTP methods that are eligible for retrying. **Default:**
`['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`.
`['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY']`.
* `statusCodes` {number[]} HTTP status codes that trigger a retry. **Default:**
`[500, 502, 503, 504, 429]`.
* `errorCodes` {string[]} Network error codes that trigger a retry. **Default:**
Expand Down
2 changes: 1 addition & 1 deletion deps/undici/src/docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ const response = await fetch('https://api.example.com/data');
- Building isomorphic code that runs in browsers and Node.js
- Publishing to npm and want to maximize compatibility with JS runtimes
- Simple HTTP requests without advanced configuration
- You're publishing to npm and you want to maximize compatiblity
- You're publishing to npm and you want to maximize compatibility
- You don't depend on features from a specific version of undici

#### Use Undici Module When:
Expand Down
70 changes: 58 additions & 12 deletions deps/undici/src/lib/cache/sqlite-cache-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const { zstdCompressSync, zstdDecompressSync } = require('node:zlib')

let DatabaseSync

const VERSION = 4
// Bump when entries written by an older cache policy are unsafe to reuse.
// Version 5 invalidates entries that may have been poisoned through redirects.
const VERSION = 5

// 2gb
const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
Expand All @@ -18,6 +20,20 @@ const COMPRESS_THRESHOLD = 1024 * 1024
// node:sqlite is available.
const hasZstd = typeof zstdCompressSync === 'function' && typeof zstdDecompressSync === 'function'

// SQLite reports a database that another connection is writing as SQLITE_BUSY
// (5) or SQLITE_LOCKED (6).
// https://www.sqlite.org/rescode.html#busy
const SQLITE_BUSY = 5
const SQLITE_LOCKED = 6

/**
* @param {Error & { errcode?: number }} err
* @returns {boolean}
*/
function isLockedError (err) {
return err?.errcode === SQLITE_BUSY || err?.errcode === SQLITE_LOCKED
}

/**
* @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
* @implements {CacheStore}
Expand Down Expand Up @@ -259,7 +275,7 @@ module.exports = class SqliteCacheStore {
get (key) {
assertCacheKey(key)

const value = this.#findValue(key)
const value = this.#lockSafe(() => this.#findValue(key))
return value
? {
body: value.body
Expand Down Expand Up @@ -307,10 +323,10 @@ module.exports = class SqliteCacheStore {
}
}

const existingValue = this.#findValue(key, true)
const existingValue = this.#lockSafe(() => this.#findValue(key, true))
if (existingValue) {
// Updating an existing response, let's overwrite it
this.#updateValueQuery.run(
this.#lockSafe(() => this.#updateValueQuery.run(
storedBody,
compressed,
value.deleteAt,
Expand All @@ -323,10 +339,10 @@ module.exports = class SqliteCacheStore {
value.cachedAt,
value.staleAt,
existingValue.id
)
))
} else {
// New response, let's insert it
this.#insertValueQuery.run(
this.#lockSafe(() => this.#insertValueQuery.run(
url,
key.method,
storedBody,
Expand All @@ -340,7 +356,7 @@ module.exports = class SqliteCacheStore {
value.vary ? JSON.stringify(value.vary) : null,
value.cachedAt,
value.staleAt
)
))
this.#prune()
}
}
Expand Down Expand Up @@ -389,23 +405,53 @@ module.exports = class SqliteCacheStore {
throw new TypeError(`expected key to be object, got ${typeof key}`)
}

this.#deleteByUrlQuery.run(this.#makeValueUrl(key))
this.#lockSafe(() => this.#deleteByUrlQuery.run(this.#makeValueUrl(key)))
}

/**
* Runs a database operation, skipping it when another connection holds the
* database lock.
*
* The cache store is meant to be shared between processes, and SQLite reports
* a concurrent writer as SQLITE_BUSY/SQLITE_LOCKED. Those errors are
* transient, and caching is best effort: the entry is skipped and the next
* request retries, rather than failing the request that triggered the write.
*
* @template T
* @param {() => T} fn
* @returns {T | undefined}
*/
#lockSafe (fn) {
try {
return fn()
} catch (err) {
if (!isLockedError(err)) {
throw err
}

return undefined
}
}

#prune () {
if (Number.isFinite(this.#maxCount) && this.size <= this.#maxCount) {
return 0
if (Number.isFinite(this.#maxCount)) {
// A locked database cannot be measured, so skip pruning instead of
// failing the write that triggered it.
const size = this.#lockSafe(() => this.size)
if (size === undefined || size <= this.#maxCount) {
return 0
}
}

{
const removed = this.#deleteExpiredValuesQuery.run(Date.now()).changes
const removed = this.#lockSafe(() => this.#deleteExpiredValuesQuery.run(Date.now()).changes)
if (removed) {
return removed
}
}

{
const removed = this.#deleteOldValuesQuery?.run(Math.max(Math.floor(this.#maxCount * 0.1), 1)).changes
const removed = this.#lockSafe(() => this.#deleteOldValuesQuery?.run(Math.max(Math.floor(this.#maxCount * 0.1), 1)).changes)
if (removed) {
return removed
}
Expand Down
4 changes: 3 additions & 1 deletion deps/undici/src/lib/dispatcher/client-h1.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ function lazyllhttp () {

// We disable wasm SIMD on older versions of Node.js on ppc64 that are broken on Power >=9 architectures.
let useWasmSIMD = true
if (process.arch === 'ppc64') {
if (process.arch === 'riscv64') {
useWasmSIMD = false
} else if (process.arch === 'ppc64') {
const [major, minor] = process.versions.node.split('.').map(n => parseInt(n, 10))
if (major < 24 || (major === 24 && minor < 12)) {
useWasmSIMD = false
Expand Down
Loading
Loading