aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib/handler/cache-handler.js
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-14 14:46:37 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-14 14:46:37 -0800
commitafa87af01c79a9baa539f2992d32154d2a4739bd (patch)
tree92c7416db734270a2fee1d72ee9cc119379ff8e1 /vanilla/node_modules/undici/lib/handler/cache-handler.js
parent3b927e84d200402281f68181cd4253bc77e5528d (diff)
downloadneko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.gz
neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.bz2
neko-afa87af01c79a9baa539f2992d32154d2a4739bd.zip
task: delete vanilla js prototype\n\n- Removed vanilla/ directory and web/dist/vanilla directory\n- Updated Makefile, Dockerfile, and CI workflow to remove vanilla references\n- Cleaned up web/web.go to remove vanilla embed and routes\n- Verified build and tests pass\n\nCloses NK-2tcnmq
Diffstat (limited to 'vanilla/node_modules/undici/lib/handler/cache-handler.js')
-rw-r--r--vanilla/node_modules/undici/lib/handler/cache-handler.js561
1 files changed, 0 insertions, 561 deletions
diff --git a/vanilla/node_modules/undici/lib/handler/cache-handler.js b/vanilla/node_modules/undici/lib/handler/cache-handler.js
deleted file mode 100644
index 93a70e8..0000000
--- a/vanilla/node_modules/undici/lib/handler/cache-handler.js
+++ /dev/null
@@ -1,561 +0,0 @@
-'use strict'
-
-const util = require('../core/util')
-const {
- parseCacheControlHeader,
- parseVaryHeader,
- isEtagUsable
-} = require('../util/cache')
-const { parseHttpDate } = require('../util/date.js')
-
-function noop () {}
-
-// Status codes that we can use some heuristics on to cache
-const HEURISTICALLY_CACHEABLE_STATUS_CODES = [
- 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501
-]
-
-// Status codes which semantic is not handled by the cache
-// https://datatracker.ietf.org/doc/html/rfc9111#section-3
-// This list should not grow beyond 206 unless the RFC is updated
-// by a newer one including more. Please introduce another list if
-// implementing caching of responses with the 'must-understand' directive.
-const NOT_UNDERSTOOD_STATUS_CODES = [
- 206
-]
-
-const MAX_RESPONSE_AGE = 2147483647000
-
-/**
- * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler
- *
- * @implements {DispatchHandler}
- */
-class CacheHandler {
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}
- */
- #cacheKey
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions['type']}
- */
- #cacheType
-
- /**
- * @type {number | undefined}
- */
- #cacheByDefault
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheStore}
- */
- #store
-
- /**
- * @type {import('../../types/dispatcher.d.ts').default.DispatchHandler}
- */
- #handler
-
- /**
- * @type {import('node:stream').Writable | undefined}
- */
- #writeStream
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} opts
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
- * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
- */
- constructor ({ store, type, cacheByDefault }, cacheKey, handler) {
- this.#store = store
- this.#cacheType = type
- this.#cacheByDefault = cacheByDefault
- this.#cacheKey = cacheKey
- this.#handler = handler
- }
-
- onRequestStart (controller, context) {
- this.#writeStream?.destroy()
- this.#writeStream = undefined
- this.#handler.onRequestStart?.(controller, context)
- }
-
- onRequestUpgrade (controller, statusCode, headers, socket) {
- this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
- }
-
- /**
- * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
- * @param {number} statusCode
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {string} statusMessage
- */
- onResponseStart (
- controller,
- statusCode,
- resHeaders,
- statusMessage
- ) {
- const downstreamOnHeaders = () =>
- this.#handler.onResponseStart?.(
- controller,
- statusCode,
- resHeaders,
- statusMessage
- )
- const handler = this
-
- if (
- !util.safeHTTPMethods.includes(this.#cacheKey.method) &&
- statusCode >= 200 &&
- statusCode <= 399
- ) {
- // Successful response to an unsafe method, delete it from cache
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-response
- try {
- this.#store.delete(this.#cacheKey)?.catch?.(noop)
- } catch {
- // Fail silently
- }
- return downstreamOnHeaders()
- }
-
- const cacheControlHeader = resHeaders['cache-control']
- const heuristicallyCacheable = resHeaders['last-modified'] && HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode)
- if (
- !cacheControlHeader &&
- !resHeaders['expires'] &&
- !heuristicallyCacheable &&
- !this.#cacheByDefault
- ) {
- // Don't have anything to tell us this response is cachable and we're not
- // caching by default
- return downstreamOnHeaders()
- }
-
- const cacheControlDirectives = cacheControlHeader ? parseCacheControlHeader(cacheControlHeader) : {}
- if (!canCacheResponse(this.#cacheType, statusCode, resHeaders, cacheControlDirectives)) {
- return downstreamOnHeaders()
- }
-
- const now = Date.now()
- const resAge = resHeaders.age ? getAge(resHeaders.age) : undefined
- if (resAge && resAge >= MAX_RESPONSE_AGE) {
- // Response considered stale
- return downstreamOnHeaders()
- }
-
- const resDate = typeof resHeaders.date === 'string'
- ? parseHttpDate(resHeaders.date)
- : undefined
-
- const staleAt =
- determineStaleAt(this.#cacheType, now, resAge, resHeaders, resDate, cacheControlDirectives) ??
- this.#cacheByDefault
- if (staleAt === undefined || (resAge && resAge > staleAt)) {
- return downstreamOnHeaders()
- }
-
- const baseTime = resDate ? resDate.getTime() : now
- const absoluteStaleAt = staleAt + baseTime
- if (now >= absoluteStaleAt) {
- // Response is already stale
- return downstreamOnHeaders()
- }
-
- let varyDirectives
- if (this.#cacheKey.headers && resHeaders.vary) {
- varyDirectives = parseVaryHeader(resHeaders.vary, this.#cacheKey.headers)
- if (!varyDirectives) {
- // Parse error
- return downstreamOnHeaders()
- }
- }
-
- const deleteAt = determineDeleteAt(baseTime, cacheControlDirectives, absoluteStaleAt)
- const strippedHeaders = stripNecessaryHeaders(resHeaders, cacheControlDirectives)
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}
- */
- const value = {
- statusCode,
- statusMessage,
- headers: strippedHeaders,
- vary: varyDirectives,
- cacheControlDirectives,
- cachedAt: resAge ? now - resAge : now,
- staleAt: absoluteStaleAt,
- deleteAt
- }
-
- // Not modified, re-use the cached value
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-304-not-modified
- if (statusCode === 304) {
- const handle304 = (cachedValue) => {
- if (!cachedValue) {
- // Do not create a new cache entry, as a 304 won't have a body - so cannot be cached.
- return downstreamOnHeaders()
- }
-
- // Re-use the cached value: statuscode, statusmessage, headers and body
- value.statusCode = cachedValue.statusCode
- value.statusMessage = cachedValue.statusMessage
- value.etag = cachedValue.etag
- value.headers = { ...cachedValue.headers, ...strippedHeaders }
-
- downstreamOnHeaders()
-
- this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)
-
- if (!this.#writeStream || !cachedValue?.body) {
- return
- }
-
- if (typeof cachedValue.body.values === 'function') {
- const bodyIterator = cachedValue.body.values()
-
- const streamCachedBody = () => {
- for (const chunk of bodyIterator) {
- const full = this.#writeStream.write(chunk) === false
- this.#handler.onResponseData?.(controller, chunk)
- // when stream is full stop writing until we get a 'drain' event
- if (full) {
- break
- }
- }
- }
-
- this.#writeStream
- .on('error', function () {
- handler.#writeStream = undefined
- handler.#store.delete(handler.#cacheKey)
- })
- .on('drain', () => {
- streamCachedBody()
- })
- .on('close', function () {
- if (handler.#writeStream === this) {
- handler.#writeStream = undefined
- }
- })
-
- streamCachedBody()
- } else if (typeof cachedValue.body.on === 'function') {
- // Readable stream body (e.g. from async/remote cache stores)
- cachedValue.body
- .on('data', (chunk) => {
- this.#writeStream.write(chunk)
- this.#handler.onResponseData?.(controller, chunk)
- })
- .on('end', () => {
- this.#writeStream.end()
- })
- .on('error', () => {
- this.#writeStream = undefined
- this.#store.delete(this.#cacheKey)
- })
-
- this.#writeStream
- .on('error', function () {
- handler.#writeStream = undefined
- handler.#store.delete(handler.#cacheKey)
- })
- .on('close', function () {
- if (handler.#writeStream === this) {
- handler.#writeStream = undefined
- }
- })
- }
- }
-
- /**
- * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}
- */
- const result = this.#store.get(this.#cacheKey)
- if (result && typeof result.then === 'function') {
- result.then(handle304)
- } else {
- handle304(result)
- }
- } else {
- if (typeof resHeaders.etag === 'string' && isEtagUsable(resHeaders.etag)) {
- value.etag = resHeaders.etag
- }
-
- this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)
-
- if (!this.#writeStream) {
- return downstreamOnHeaders()
- }
-
- this.#writeStream
- .on('drain', () => controller.resume())
- .on('error', function () {
- // TODO (fix): Make error somehow observable?
- handler.#writeStream = undefined
-
- // Delete the value in case the cache store is holding onto state from
- // the call to createWriteStream
- handler.#store.delete(handler.#cacheKey)
- })
- .on('close', function () {
- if (handler.#writeStream === this) {
- handler.#writeStream = undefined
- }
-
- // TODO (fix): Should we resume even if was paused downstream?
- controller.resume()
- })
-
- downstreamOnHeaders()
- }
- }
-
- onResponseData (controller, chunk) {
- if (this.#writeStream?.write(chunk) === false) {
- controller.pause()
- }
-
- this.#handler.onResponseData?.(controller, chunk)
- }
-
- onResponseEnd (controller, trailers) {
- this.#writeStream?.end()
- this.#handler.onResponseEnd?.(controller, trailers)
- }
-
- onResponseError (controller, err) {
- this.#writeStream?.destroy(err)
- this.#writeStream = undefined
- this.#handler.onResponseError?.(controller, err)
- }
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
- *
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
- * @param {number} statusCode
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- */
-function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirectives) {
- // Status code must be final and understood.
- if (statusCode < 200 || NOT_UNDERSTOOD_STATUS_CODES.includes(statusCode)) {
- return false
- }
- // Responses with neither status codes that are heuristically cacheable, nor "explicit enough" caching
- // directives, are not cacheable. "Explicit enough": see https://www.rfc-editor.org/rfc/rfc9111.html#section-3
- if (!HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode) && !resHeaders['expires'] &&
- !cacheControlDirectives.public &&
- cacheControlDirectives['max-age'] === undefined &&
- // RFC 9111: a private response directive, if the cache is not shared
- !(cacheControlDirectives.private && cacheType === 'private') &&
- !(cacheControlDirectives['s-maxage'] !== undefined && cacheType === 'shared')
- ) {
- return false
- }
-
- if (cacheControlDirectives['no-store']) {
- return false
- }
-
- if (cacheType === 'shared' && cacheControlDirectives.private === true) {
- return false
- }
-
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1-5
- if (resHeaders.vary?.includes('*')) {
- return false
- }
-
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
- if (resHeaders.authorization) {
- if (!cacheControlDirectives.public || typeof resHeaders.authorization !== 'string') {
- return false
- }
-
- if (
- Array.isArray(cacheControlDirectives['no-cache']) &&
- cacheControlDirectives['no-cache'].includes('authorization')
- ) {
- return false
- }
-
- if (
- Array.isArray(cacheControlDirectives['private']) &&
- cacheControlDirectives['private'].includes('authorization')
- ) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @param {string | string[]} ageHeader
- * @returns {number | undefined}
- */
-function getAge (ageHeader) {
- const age = parseInt(Array.isArray(ageHeader) ? ageHeader[0] : ageHeader)
-
- return isNaN(age) ? undefined : age * 1000
-}
-
-/**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
- * @param {number} now
- * @param {number | undefined} age
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {Date | undefined} responseDate
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- *
- * @returns {number | undefined} time that the value is stale at in seconds or undefined if it shouldn't be cached
- */
-function determineStaleAt (cacheType, now, age, resHeaders, responseDate, cacheControlDirectives) {
- if (cacheType === 'shared') {
- // Prioritize s-maxage since we're a shared cache
- // s-maxage > max-age > Expire
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10-3
- const sMaxAge = cacheControlDirectives['s-maxage']
- if (sMaxAge !== undefined) {
- return sMaxAge > 0 ? sMaxAge * 1000 : undefined
- }
- }
-
- const maxAge = cacheControlDirectives['max-age']
- if (maxAge !== undefined) {
- return maxAge > 0 ? maxAge * 1000 : undefined
- }
-
- if (typeof resHeaders.expires === 'string') {
- // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
- const expiresDate = parseHttpDate(resHeaders.expires)
- if (expiresDate) {
- if (now >= expiresDate.getTime()) {
- return undefined
- }
-
- if (responseDate) {
- if (responseDate >= expiresDate) {
- return undefined
- }
-
- if (age !== undefined && age > (expiresDate - responseDate)) {
- return undefined
- }
- }
-
- return expiresDate.getTime() - now
- }
- }
-
- if (typeof resHeaders['last-modified'] === 'string') {
- // https://www.rfc-editor.org/rfc/rfc9111.html#name-calculating-heuristic-fresh
- const lastModified = new Date(resHeaders['last-modified'])
- if (isValidDate(lastModified)) {
- if (lastModified.getTime() >= now) {
- return undefined
- }
-
- const responseAge = now - lastModified.getTime()
-
- return responseAge * 0.1
- }
- }
-
- if (cacheControlDirectives.immutable) {
- // https://www.rfc-editor.org/rfc/rfc8246.html#section-2.2
- return 31536000
- }
-
- return undefined
-}
-
-/**
- * @param {number} now
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- * @param {number} staleAt
- */
-function determineDeleteAt (now, cacheControlDirectives, staleAt) {
- let staleWhileRevalidate = -Infinity
- let staleIfError = -Infinity
- let immutable = -Infinity
-
- if (cacheControlDirectives['stale-while-revalidate']) {
- staleWhileRevalidate = staleAt + (cacheControlDirectives['stale-while-revalidate'] * 1000)
- }
-
- if (cacheControlDirectives['stale-if-error']) {
- staleIfError = staleAt + (cacheControlDirectives['stale-if-error'] * 1000)
- }
-
- if (staleWhileRevalidate === -Infinity && staleIfError === -Infinity) {
- immutable = now + 31536000000
- }
-
- return Math.max(staleAt, staleWhileRevalidate, staleIfError, immutable)
-}
-
-/**
- * Strips headers required to be removed in cached responses
- * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
- * @returns {Record<string, string | string []>}
- */
-function stripNecessaryHeaders (resHeaders, cacheControlDirectives) {
- const headersToRemove = [
- 'connection',
- 'proxy-authenticate',
- 'proxy-authentication-info',
- 'proxy-authorization',
- 'proxy-connection',
- 'te',
- 'transfer-encoding',
- 'upgrade',
- // We'll add age back when serving it
- 'age'
- ]
-
- if (resHeaders['connection']) {
- if (Array.isArray(resHeaders['connection'])) {
- // connection: a
- // connection: b
- headersToRemove.push(...resHeaders['connection'].map(header => header.trim()))
- } else {
- // connection: a, b
- headersToRemove.push(...resHeaders['connection'].split(',').map(header => header.trim()))
- }
- }
-
- if (Array.isArray(cacheControlDirectives['no-cache'])) {
- headersToRemove.push(...cacheControlDirectives['no-cache'])
- }
-
- if (Array.isArray(cacheControlDirectives['private'])) {
- headersToRemove.push(...cacheControlDirectives['private'])
- }
-
- let strippedHeaders
- for (const headerName of headersToRemove) {
- if (resHeaders[headerName]) {
- strippedHeaders ??= { ...resHeaders }
- delete strippedHeaders[headerName]
- }
- }
-
- return strippedHeaders ?? resHeaders
-}
-
-/**
- * @param {Date} date
- * @returns {boolean}
- */
-function isValidDate (date) {
- return date instanceof Date && Number.isFinite(date.valueOf())
-}
-
-module.exports = CacheHandler