跳转到内容

源码

以下是 DoHflare 的完整脚本 _worker.js。您可直接复制下方代码进行部署,或下载完整项目压缩包。详细部署步骤请参阅部署指南

js
/*
 * DoHflare - High-Performance DNS over HTTPS Edge Proxy
 * Copyright (C) 2026  Racpast <https://github.com/Racpast/DoHflare>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 *
 * ADDITIONAL NOTICE: Pursuant to Section 7 of the GNU AGPLv3,
 * additional terms apply to this work. Refer to the NOTICE file
 * in the repository for details regarding attribution requirements,
 * trademark disclaimers, and compliance policies.
 */

/* ==========================================================================
   1. ISOLATE LEVEL CONSTANTS & RUNTIME DEFAULTS
   ========================================================================== */
const ISOLATE_CONSTANTS = {
  MEMORY_CACHE_LIMIT: 2000,
  MEMORY_CACHE_SIZE_BYTES: 8 * 1024 * 1024,
};

const RUNTIME_DEFAULTS = {
  UPSTREAM_URLS: [
    "https://dns.google/dns-query",
    "https://dns11.quad9.net/dns-query",
  ],
  DOH_CONTENT_TYPE: "application/dns-message",
  DOH_PATH: "/dns-query",

  ROOT_CONTENT: null,
  ROOT_CONTENT_TYPE: "text/html; charset=utf-8",
  ROOT_CACHE_TTL: 86400,

  MAX_CACHEABLE_BYTES: 64 * 1024,
  MAX_POST_BODY_BYTES: 8 * 1024,
  FETCH_TIMEOUT_MS: 2500,
  MAX_RETRIES: 2,

  DEFAULT_POSITIVE_TTL: 60,
  DEFAULT_NEGATIVE_TTL: 15,
  FALLBACK_ECS_IP: "119.29.29.0",

  CF_CACHE_WRITE_THRESHOLD: 500,
  GLOBAL_WRITE_COOLDOWN_MS: 5 * 60 * 1000,
  GLOBAL_WRITE_PER_MINUTE_LIMIT: 200,

  HOT_WINDOW_MS: 60 * 1000,
  HOT_HIT_THRESHOLD: 20,

  STALE_WINDOW_FACTOR: 0.5,
  EXTREME_STALE_FALLBACK_MS: 24 * 3600 * 1000,
  JITTER_PCT: 10,

  GLOBAL_CACHE_NAMESPACE: "https://dohflare.local/cache/",
};

/* ==========================================================================
   2. DNS PROTOCOL CONSTANTS & ERROR CODES
   ========================================================================== */
const DNS_CONSTANTS = {
  HEADER_LEN: 12,
  OFFSET_ID: 0,
  OFFSET_QDCOUNT: 4,
  OFFSET_ANCOUNT: 6,
  OFFSET_NSCOUNT: 8,
  OFFSET_ARCOUNT: 10,
  TYPE_OPT: 41,
  OPT_CODE_ECS: 8,
  RCODE_NXDOMAIN: 3,
  MAX_NAME_ITERATIONS: 130,
  OPT_HEADER_TEMPLATE: new Uint8Array([
    0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  ]),
};

const ERRORS = {
  OOB: "OOB_READ",
  LOOP: "MALFORMED_LOOP",
  TIMEOUT: "UPSTREAM_TIMEOUT",
  OVERSIZED: "PAYLOAD_TOO_LARGE",
};

/* ==========================================================================
   3. UTILITIES & HASH FUNCTIONS
   ========================================================================== */
function generateFnv1a32Hex(dataView) {
  let hashValue = 0x811c9dc5;
  for (let index = 0; index < dataView.length; index++) {
    hashValue ^= dataView[index];
    hashValue = Math.imul(hashValue, 0x01000193);
  }
  return (hashValue >>> 0).toString(16).padStart(8, "0");
}

function convertBase64UrlToBuffer(base64UrlString) {
  let base64String = base64UrlString.replace(/-/g, "+").replace(/_/g, "/");
  while (base64String.length % 4) base64String += "=";
  const decodedString = atob(base64String);
  const byteArray = new Uint8Array(decodedString.length);
  for (let index = 0; index < decodedString.length; index++)
    byteArray[index] = decodedString.charCodeAt(index);
  return byteArray.buffer;
}

function normalizeClientIp(rawIpAddress, fallbackIpAddress) {
  if (!rawIpAddress) return fallbackIpAddress;
  rawIpAddress = rawIpAddress.trim();
  const colonIndex = rawIpAddress.indexOf(":");
  if (colonIndex !== -1 && rawIpAddress.split(":").length > 2)
    return rawIpAddress;
  if (colonIndex !== -1) return rawIpAddress.split(":")[0];
  return rawIpAddress;
}

async function executeFetchWithTimeout(
  requestUrl,
  fetchOptions,
  timeoutMilliseconds,
) {
  if (typeof AbortSignal !== "undefined" && AbortSignal.timeout) {
    return fetch(requestUrl, {
      ...fetchOptions,
      signal: AbortSignal.timeout(timeoutMilliseconds),
    });
  }
  const abortController = new AbortController();
  const timeoutId = setTimeout(
    () => abortController.abort(),
    timeoutMilliseconds,
  );
  try {
    const fetchResponse = await fetch(requestUrl, {
      ...fetchOptions,
      signal: abortController.signal,
    });
    return fetchResponse;
  } finally {
    clearTimeout(timeoutId);
  }
}

/* ==========================================================================
   4. LRU CACHE & GLOBAL STATE
   ========================================================================== */
class LRUCacheNode {
  constructor(key, value) {
    this.key = key;
    this.value = value;
    this.prev = null;
    this.next = null;
  }
}

class MemoryLRUCache {
  constructor(maxItemCount, maxSizeBytes) {
    this.maxItemCount = maxItemCount;
    this.maxSizeBytes = maxSizeBytes;
    this.nodeMap = new Map();
    this.headNode = null;
    this.tailNode = null;
    this.currentSizeBytes = 0;
  }

  peek(cacheKey) {
    const targetNode = this.nodeMap.get(cacheKey);
    return targetNode ? targetNode.value : null;
  }

  get(cacheKey, extremeStaleMilliseconds) {
    const targetNode = this.nodeMap.get(cacheKey);
    if (!targetNode) return null;
    if (
      Date.now() >
      targetNode.value.expiryTimestamp + extremeStaleMilliseconds
    ) {
      this.delete(cacheKey);
      return null;
    }
    this._moveNodeToHead(targetNode);
    return targetNode.value;
  }

  set(cacheKey, cacheValue, extremeStaleMilliseconds) {
    const existingNode = this.nodeMap.get(cacheKey);
    if (existingNode) {
      const oldCacheValue = existingNode.value;
      cacheValue.hitTimestamps = oldCacheValue.hitTimestamps;
      cacheValue.currentHitIndex = oldCacheValue.currentHitIndex;
      cacheValue.totalHitCount = oldCacheValue.totalHitCount;
      cacheValue.lastGlobalWriteTimestamp =
        oldCacheValue.lastGlobalWriteTimestamp ||
        cacheValue.lastGlobalWriteTimestamp;
      this.delete(cacheKey);
    }
    cacheValue.hardExpiryTimestamp =
      cacheValue.expiryTimestamp + extremeStaleMilliseconds;
    const newNode = new LRUCacheNode(cacheKey, cacheValue);
    this._addNodeToHead(newNode);
    this.nodeMap.set(cacheKey, newNode);
    this.currentSizeBytes += cacheValue.payloadSize || 0;
    this._evictStaleNodes();
  }

  delete(cacheKey) {
    const targetNode = this.nodeMap.get(cacheKey);
    if (!targetNode) return;
    this._removeNode(targetNode);
    this.nodeMap.delete(cacheKey);
    this.currentSizeBytes = Math.max(
      0,
      this.currentSizeBytes - (targetNode.value.payloadSize || 0),
    );
  }

  _evictStaleNodes() {
    while (
      (this.nodeMap.size > this.maxItemCount ||
        this.currentSizeBytes > this.maxSizeBytes) &&
      this.tailNode
    ) {
      this.delete(this.tailNode.key);
    }
  }

  _addNodeToHead(targetNode) {
    targetNode.next = this.headNode;
    targetNode.prev = null;
    if (this.headNode) this.headNode.prev = targetNode;
    this.headNode = targetNode;
    if (!this.tailNode) this.tailNode = targetNode;
  }

  _removeNode(targetNode) {
    if (targetNode.prev) targetNode.prev.next = targetNode.next;
    if (targetNode.next) targetNode.next.prev = targetNode.prev;
    if (this.headNode === targetNode) this.headNode = targetNode.next;
    if (this.tailNode === targetNode) this.tailNode = targetNode.prev;
    targetNode.prev = targetNode.next = null;
  }

  _moveNodeToHead(targetNode) {
    this._removeNode(targetNode);
    this._addNodeToHead(targetNode);
  }
}

const primaryMemoryCache = new MemoryLRUCache(
  ISOLATE_CONSTANTS.MEMORY_CACHE_LIMIT,
  ISOLATE_CONSTANTS.MEMORY_CACHE_SIZE_BYTES,
);
const requestCoalescingMap = new Map();
const activeGlobalWriteLocks = new Set();
let rateLimitWindowStartTimestamp = 0;
let rateLimitWriteCount = 0;

/* ==========================================================================
   5. DNS PACKET PARSER & MANIPULATOR
   ========================================================================== */
class DnsPacketProcessor {
  static skipDnsNameSafely(dataView, startOffset) {
    let currentOffset = startOffset;
    let iterationCount = 0;
    while (currentOffset < dataView.byteLength) {
      if (iterationCount++ > DNS_CONSTANTS.MAX_NAME_ITERATIONS)
        throw new Error(ERRORS.LOOP);
      const labelLength = dataView.getUint8(currentOffset);
      if (labelLength === 0) return currentOffset + 1;
      if ((labelLength & 0xc0) === 0xc0) {
        if (currentOffset + 2 > dataView.byteLength)
          throw new Error(ERRORS.OOB);
        return currentOffset + 2;
      }
      currentOffset += labelLength + 1;
      if (currentOffset > dataView.byteLength) throw new Error(ERRORS.OOB);
    }
    throw new Error(ERRORS.OOB);
  }

  static verifyEcsOptionPresence(packetBuffer) {
    try {
      const dataView = new DataView(packetBuffer);
      if (packetBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) return false;
      let currentOffset = DNS_CONSTANTS.HEADER_LEN;
      const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT);
      const answerCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT);
      const authorityCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT);
      const additionalCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT);
      for (let index = 0; index < questionCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        currentOffset += 4;
      }
      for (let index = 0; index < answerCount + authorityCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength) return false;
        currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8);
      }
      for (let index = 0; index < additionalCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength) return false;
        const recordType = dataView.getUint16(currentOffset);
        const recordDataLength = dataView.getUint16(currentOffset + 8);
        currentOffset += 10;
        if (recordType === DNS_CONSTANTS.TYPE_OPT) {
          let optionOffset = currentOffset;
          const optionEndOffset = currentOffset + recordDataLength;
          if (optionEndOffset > dataView.byteLength) return false;
          while (optionOffset + 4 <= optionEndOffset) {
            if (dataView.getUint16(optionOffset) === DNS_CONSTANTS.OPT_CODE_ECS)
              return true;
            optionOffset += 4 + dataView.getUint16(optionOffset + 2);
          }
        }
        currentOffset += recordDataLength;
      }
      return false;
    } catch {
      return false;
    }
  }

  static constructEcsOptionPayload(clientIpAddress, fallbackIpAddress) {
    const targetIp = clientIpAddress || fallbackIpAddress;
    const isIPv4Format = targetIp.indexOf(":") === -1;
    if (isIPv4Format) {
      const ipOctets = targetIp
        .split(".")
        .map((octet) => Math.max(0, Math.min(255, Number(octet) || 0)));
      const payloadBytes = [ipOctets[0], ipOctets[1], ipOctets[2]];
      const totalOptionLength = 4 + payloadBytes.length;
      const optionBuffer = new Uint8Array(4 + totalOptionLength);
      optionBuffer.set(
        [
          0x00,
          DNS_CONSTANTS.OPT_CODE_ECS,
          (totalOptionLength >> 8) & 0xff,
          totalOptionLength & 0xff,
          0x00,
          0x01,
          24,
          0x00,
        ],
        0,
      );
      optionBuffer.set(payloadBytes, 8);
      return optionBuffer;
    } else {
      const ipSegments = targetIp.split("::");
      const leftSegments = ipSegments[0]
        ? ipSegments[0].split(":").filter(Boolean)
        : [];
      const rightSegments =
        ipSegments.length > 1 && ipSegments[1]
          ? ipSegments[1].split(":").filter(Boolean)
          : [];
      const leftNumericSegments = leftSegments.map(
        (hexStr) => parseInt(hexStr, 16) || 0,
      );
      const rightNumericSegments = rightSegments.map(
        (hexStr) => parseInt(hexStr, 16) || 0,
      );
      const combinedSegments = [...leftNumericSegments];
      const missingSegmentsCount = Math.max(
        0,
        8 - (leftNumericSegments.length + rightNumericSegments.length),
      );
      for (let index = 0; index < missingSegmentsCount; index++)
        combinedSegments.push(0);
      combinedSegments.push(...rightNumericSegments);
      const ipByteArray = new Uint8Array(16);
      for (let index = 0; index < 8; index++) {
        ipByteArray[index * 2] = (combinedSegments[index] >> 8) & 0xff;
        ipByteArray[index * 2 + 1] = combinedSegments[index] & 0xff;
      }
      const prefixPayloadBytes = ipByteArray.slice(0, 7); // /56 prefix length
      const totalOptionLength = 4 + prefixPayloadBytes.length;
      const optionBuffer = new Uint8Array(4 + totalOptionLength);
      optionBuffer.set(
        [
          0x00,
          DNS_CONSTANTS.OPT_CODE_ECS,
          (totalOptionLength >> 8) & 0xff,
          totalOptionLength & 0xff,
          0x00,
          0x02,
          56,
          0x00,
        ],
        0,
      );
      optionBuffer.set(prefixPayloadBytes, 8);
      return optionBuffer;
    }
  }

  static injectEcsPayload(
    originalQueryBuffer,
    clientIpAddress,
    fallbackIpAddress,
  ) {
    try {
      if (this.verifyEcsOptionPresence(originalQueryBuffer))
        return originalQueryBuffer;
      const dataView = new DataView(originalQueryBuffer);
      if (originalQueryBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN)
        return originalQueryBuffer;

      const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT);
      const answerCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT);
      const authorityCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT);
      const additionalCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT);
      let currentOffset = DNS_CONSTANTS.HEADER_LEN;

      for (let index = 0; index < questionCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        currentOffset += 4;
      }
      for (let index = 0; index < answerCount + authorityCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength)
          throw new Error(ERRORS.OOB);
        currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8);
      }

      let targetRecordOffset = -1;
      let targetDataLengthOffset = -1;
      let targetDataLength = 0;

      for (let index = 0; index < additionalCount; index++) {
        const recordStartOffset = currentOffset;
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength)
          throw new Error(ERRORS.OOB);
        const recordType = dataView.getUint16(currentOffset);
        if (recordType === DNS_CONSTANTS.TYPE_OPT) {
          targetRecordOffset = recordStartOffset;
          targetDataLengthOffset = currentOffset + 8;
          targetDataLength = dataView.getUint16(targetDataLengthOffset);
          break;
        }
        currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8);
      }

      const ecsOptionBuffer = this.constructEcsOptionPayload(
        clientIpAddress,
        fallbackIpAddress,
      );

      if (targetRecordOffset !== -1) {
        const insertionPosition = targetDataLengthOffset + 2 + targetDataLength;
        if (insertionPosition > originalQueryBuffer.byteLength)
          throw new Error(ERRORS.OOB);
        const modifiedBufferLength =
          originalQueryBuffer.byteLength + ecsOptionBuffer.length;
        const modifiedBuffer = new Uint8Array(modifiedBufferLength);
        modifiedBuffer.set(
          new Uint8Array(originalQueryBuffer, 0, insertionPosition),
          0,
        );
        modifiedBuffer.set(ecsOptionBuffer, insertionPosition);
        modifiedBuffer.set(
          new Uint8Array(originalQueryBuffer, insertionPosition),
          insertionPosition + ecsOptionBuffer.length,
        );
        new DataView(modifiedBuffer.buffer).setUint16(
          targetDataLengthOffset,
          targetDataLength + ecsOptionBuffer.length,
        );
        return modifiedBuffer.buffer;
      } else {
        const insertionPosition = originalQueryBuffer.byteLength;
        const modifiedBufferLength =
          insertionPosition +
          DNS_CONSTANTS.OPT_HEADER_TEMPLATE.length +
          ecsOptionBuffer.length;
        const modifiedBuffer = new Uint8Array(modifiedBufferLength);
        modifiedBuffer.set(new Uint8Array(originalQueryBuffer), 0);
        modifiedBuffer.set(
          DNS_CONSTANTS.OPT_HEADER_TEMPLATE,
          insertionPosition,
        );
        const modifiedDataView = new DataView(modifiedBuffer.buffer);
        modifiedDataView.setUint16(
          insertionPosition + DNS_CONSTANTS.OPT_HEADER_TEMPLATE.length - 2,
          ecsOptionBuffer.length,
        );
        modifiedBuffer.set(
          ecsOptionBuffer,
          insertionPosition + DNS_CONSTANTS.OPT_HEADER_TEMPLATE.length,
        );
        modifiedDataView.setUint16(
          DNS_CONSTANTS.OFFSET_ARCOUNT,
          (additionalCount + 1) & 0xffff,
        );
        return modifiedBuffer.buffer;
      }
    } catch {
      return originalQueryBuffer;
    }
  }

  static stripEcsPayload(responseBuffer) {
    try {
      const dataView = new DataView(responseBuffer);
      if (responseBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN)
        return responseBuffer;
      const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT);
      const answerCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT);
      const authorityCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT);
      const additionalCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT);
      let currentOffset = DNS_CONSTANTS.HEADER_LEN;
      for (let index = 0; index < questionCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        currentOffset += 4;
      }
      for (let index = 0; index < answerCount + authorityCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength)
          throw new Error(ERRORS.OOB);
        currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8);
      }

      for (let index = 0; index < additionalCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength) break;
        const recordType = dataView.getUint16(currentOffset);
        const dataLengthOffset = currentOffset + 8;
        const recordDataLength = dataView.getUint16(dataLengthOffset);
        if (recordType === DNS_CONSTANTS.TYPE_OPT) {
          let innerOptionOffset = dataLengthOffset + 2;
          const innerOptionEnd = innerOptionOffset + recordDataLength;
          if (innerOptionEnd > dataView.byteLength) throw new Error(ERRORS.OOB);
          let ecsDataOffset = -1;
          let ecsDataLength = 0;

          while (innerOptionOffset + 4 <= innerOptionEnd) {
            const optionCode = dataView.getUint16(innerOptionOffset);
            const optionLength = dataView.getUint16(innerOptionOffset + 2);
            if (optionCode === DNS_CONSTANTS.OPT_CODE_ECS) {
              ecsDataOffset = innerOptionOffset;
              ecsDataLength = 4 + optionLength;
              break;
            }
            innerOptionOffset += 4 + optionLength;
          }

          if (ecsDataOffset !== -1) {
            const strippedBufferLength =
              responseBuffer.byteLength - ecsDataLength;
            const strippedBuffer = new Uint8Array(strippedBufferLength);
            strippedBuffer.set(
              new Uint8Array(responseBuffer, 0, ecsDataOffset),
              0,
            );
            strippedBuffer.set(
              new Uint8Array(responseBuffer, ecsDataOffset + ecsDataLength),
              ecsDataOffset,
            );
            new DataView(strippedBuffer.buffer).setUint16(
              dataLengthOffset,
              recordDataLength - ecsDataLength,
            );
            return strippedBuffer.buffer;
          }
        }
        currentOffset += 10 + recordDataLength;
      }
      return responseBuffer;
    } catch {
      return responseBuffer;
    }
  }

  static extractTtlAndResponseCode(responseBuffer, defaultPositiveTtlValue) {
    let responseCode = 0;
    try {
      const dataView = new DataView(responseBuffer);
      if (responseBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN)
        return { extractedTtl: defaultPositiveTtlValue, responseCode };
      responseCode = dataView.getUint8(3) & 0x0f;
      let currentOffset = DNS_CONSTANTS.HEADER_LEN;
      const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT);
      const totalResourceRecords =
        dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT) +
        dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT) +
        dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT);
      for (let index = 0; index < questionCount; index++) {
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 4 > dataView.byteLength)
          return { extractedTtl: defaultPositiveTtlValue, responseCode };
        currentOffset += 4;
      }
      let minimumTtlValue = Infinity;
      for (let index = 0; index < totalResourceRecords; index++) {
        if (currentOffset >= dataView.byteLength) break;
        currentOffset = this.skipDnsNameSafely(dataView, currentOffset);
        if (currentOffset + 10 > dataView.byteLength) break;
        const recordType = dataView.getUint16(currentOffset);
        currentOffset += 4;
        const recordTtl = dataView.getUint32(currentOffset);
        currentOffset += 4;
        const recordDataLength = dataView.getUint16(currentOffset);
        currentOffset += 2;
        if (currentOffset + recordDataLength > dataView.byteLength) break;
        if (recordType !== DNS_CONSTANTS.TYPE_OPT)
          minimumTtlValue = Math.min(minimumTtlValue, recordTtl);
        currentOffset += recordDataLength;
      }
      return {
        extractedTtl:
          isFinite(minimumTtlValue) && minimumTtlValue > 0
            ? minimumTtlValue
            : defaultPositiveTtlValue,
        responseCode,
      };
    } catch {
      return { extractedTtl: defaultPositiveTtlValue, responseCode };
    }
  }

  static generateStableCacheKey(processedQueryBuffer) {
    try {
      if (processedQueryBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN)
        throw new Error("Invalid Buffer");
      const normalizedDataView = new Uint8Array(processedQueryBuffer.slice(0));
      normalizedDataView[0] = 0;
      normalizedDataView[1] = 0;
      return generateFnv1a32Hex(normalizedDataView);
    } catch {
      return generateFnv1a32Hex(new Uint8Array(processedQueryBuffer).slice(2));
    }
  }

  static patchTransactionId(payloadByteArray, transactionIdByteArray) {
    const patchedBuffer = new Uint8Array(payloadByteArray.byteLength);
    patchedBuffer.set(payloadByteArray);
    patchedBuffer[0] = transactionIdByteArray[0];
    patchedBuffer[1] = transactionIdByteArray[1];
    return patchedBuffer.buffer;
  }
}

/* ==========================================================================
   6. REQUEST HANDLER
   ========================================================================== */
class DoHRequestHandler {
  static initializeConfig(environmentVariables) {
    const parseArraySafely = (jsonString, fallbackArray) => {
      if (!jsonString) return fallbackArray;
      try {
        return JSON.parse(jsonString);
      } catch {
        return fallbackArray;
      }
    };

    return {
      UPSTREAM_URLS: parseArraySafely(
        environmentVariables.UPSTREAM_URLS,
        RUNTIME_DEFAULTS.UPSTREAM_URLS,
      ),
      DOH_CONTENT_TYPE:
        environmentVariables.DOH_CONTENT_TYPE ||
        RUNTIME_DEFAULTS.DOH_CONTENT_TYPE,
      DOH_PATH: environmentVariables.DOH_PATH || RUNTIME_DEFAULTS.DOH_PATH,
      ROOT_CONTENT:
        environmentVariables.ROOT_CONTENT || RUNTIME_DEFAULTS.ROOT_CONTENT,
      ROOT_CONTENT_TYPE:
        environmentVariables.ROOT_CONTENT_TYPE ||
        RUNTIME_DEFAULTS.ROOT_CONTENT_TYPE,
      ROOT_CACHE_TTL: environmentVariables.ROOT_CACHE_TTL
        ? parseInt(environmentVariables.ROOT_CACHE_TTL, 10)
        : RUNTIME_DEFAULTS.ROOT_CACHE_TTL,
      MAX_CACHEABLE_BYTES: environmentVariables.MAX_CACHEABLE_BYTES
        ? parseInt(environmentVariables.MAX_CACHEABLE_BYTES, 10)
        : RUNTIME_DEFAULTS.MAX_CACHEABLE_BYTES,
      MAX_POST_BODY_BYTES: environmentVariables.MAX_POST_BODY_BYTES
        ? parseInt(environmentVariables.MAX_POST_BODY_BYTES, 10)
        : RUNTIME_DEFAULTS.MAX_POST_BODY_BYTES,
      FETCH_TIMEOUT_MS: environmentVariables.FETCH_TIMEOUT_MS
        ? parseInt(environmentVariables.FETCH_TIMEOUT_MS, 10)
        : RUNTIME_DEFAULTS.FETCH_TIMEOUT_MS,
      MAX_RETRIES: environmentVariables.MAX_RETRIES
        ? parseInt(environmentVariables.MAX_RETRIES, 10)
        : RUNTIME_DEFAULTS.MAX_RETRIES,
      DEFAULT_POSITIVE_TTL: environmentVariables.DEFAULT_POSITIVE_TTL
        ? parseInt(environmentVariables.DEFAULT_POSITIVE_TTL, 10)
        : RUNTIME_DEFAULTS.DEFAULT_POSITIVE_TTL,
      DEFAULT_NEGATIVE_TTL: environmentVariables.DEFAULT_NEGATIVE_TTL
        ? parseInt(environmentVariables.DEFAULT_NEGATIVE_TTL, 10)
        : RUNTIME_DEFAULTS.DEFAULT_NEGATIVE_TTL,
      FALLBACK_ECS_IP:
        environmentVariables.FALLBACK_ECS_IP ||
        RUNTIME_DEFAULTS.FALLBACK_ECS_IP,
      CF_CACHE_WRITE_THRESHOLD: environmentVariables.CF_CACHE_WRITE_THRESHOLD
        ? parseInt(environmentVariables.CF_CACHE_WRITE_THRESHOLD, 10)
        : RUNTIME_DEFAULTS.CF_CACHE_WRITE_THRESHOLD,
      GLOBAL_WRITE_COOLDOWN_MS: environmentVariables.GLOBAL_WRITE_COOLDOWN_MS
        ? parseInt(environmentVariables.GLOBAL_WRITE_COOLDOWN_MS, 10)
        : RUNTIME_DEFAULTS.GLOBAL_WRITE_COOLDOWN_MS,
      GLOBAL_WRITE_PER_MINUTE_LIMIT:
        environmentVariables.GLOBAL_WRITE_PER_MINUTE_LIMIT
          ? parseInt(environmentVariables.GLOBAL_WRITE_PER_MINUTE_LIMIT, 10)
          : RUNTIME_DEFAULTS.GLOBAL_WRITE_PER_MINUTE_LIMIT,
      HOT_WINDOW_MS: environmentVariables.HOT_WINDOW_MS
        ? parseInt(environmentVariables.HOT_WINDOW_MS, 10)
        : RUNTIME_DEFAULTS.HOT_WINDOW_MS,
      HOT_HIT_THRESHOLD: environmentVariables.HOT_HIT_THRESHOLD
        ? parseInt(environmentVariables.HOT_HIT_THRESHOLD, 10)
        : RUNTIME_DEFAULTS.HOT_HIT_THRESHOLD,
      STALE_WINDOW_FACTOR: environmentVariables.STALE_WINDOW_FACTOR
        ? parseFloat(environmentVariables.STALE_WINDOW_FACTOR)
        : RUNTIME_DEFAULTS.STALE_WINDOW_FACTOR,
      EXTREME_STALE_FALLBACK_MS: environmentVariables.EXTREME_STALE_FALLBACK_MS
        ? parseInt(environmentVariables.EXTREME_STALE_FALLBACK_MS, 10)
        : RUNTIME_DEFAULTS.EXTREME_STALE_FALLBACK_MS,
      JITTER_PCT: environmentVariables.JITTER_PCT
        ? parseInt(environmentVariables.JITTER_PCT, 10)
        : RUNTIME_DEFAULTS.JITTER_PCT,
      GLOBAL_CACHE_NAMESPACE:
        environmentVariables.GLOBAL_CACHE_NAMESPACE ||
        RUNTIME_DEFAULTS.GLOBAL_CACHE_NAMESPACE,
    };
  }

  static checkWriteRateLimit(requestConfig) {
    const currentTimestamp = Date.now();
    if (currentTimestamp - rateLimitWindowStartTimestamp > 60 * 1000) {
      rateLimitWindowStartTimestamp = currentTimestamp;
      rateLimitWriteCount = 0;
    }
    if (rateLimitWriteCount < requestConfig.GLOBAL_WRITE_PER_MINUTE_LIMIT) {
      rateLimitWriteCount++;
      return true;
    }
    return false;
  }

  static calculateDeterministicJitter(cacheKeyHex, jitterPercentage) {
    const hashSuffixValue = parseInt(cacheKeyHex.slice(-4), 16) % 10000;
    const signedJitter =
      (hashSuffixValue / 10000) * (2 * jitterPercentage) - jitterPercentage;
    return 1 + signedJitter / 100.0;
  }

  static createResponse(
    payloadByteArray,
    transactionId,
    hasClientEcs,
    cacheAgeSeconds,
    cacheStatusIndicator,
    cacheKeyHex,
    requestConfig,
    httpStatus = 200,
    supplementalHeaders = {},
  ) {
    let finalResponseBuffer = DnsPacketProcessor.patchTransactionId(
      payloadByteArray,
      transactionId,
    );
    if (!hasClientEcs)
      finalResponseBuffer =
        DnsPacketProcessor.stripEcsPayload(finalResponseBuffer);
    const responseHeaders = {
      "Content-Type": requestConfig.DOH_CONTENT_TYPE,
      "Access-Control-Allow-Origin": "*",
      "Cache-Control": `public, max-age=${cacheAgeSeconds}`,
      "X-DOHFLARE-Cache": cacheStatusIndicator,
      "X-DOHFLARE-CacheKey": cacheKeyHex,
      "X-DOHFLARE-TTL": String(cacheAgeSeconds),
      "Content-Length": String(finalResponseBuffer.byteLength),
      ...supplementalHeaders,
    };
    return new Response(finalResponseBuffer, {
      status: httpStatus,
      headers: responseHeaders,
    });
  }

  static generateLandingPage(request, reqConfig) {
    const currentYear = new Date().getFullYear();
    const serviceHostname = new URL(request.url).hostname;
    const fullServiceEndpoint = `https://${serviceHostname}${reqConfig.DOH_PATH}`;

    return `<!doctypehtml><html lang="en"><meta charset="UTF-8"><meta content="width=device-width,initial-scale=1"name="viewport"><link href="data:image/x-icon;base64,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"rel="icon"type="image/x-icon"><title>DoHflare | Edge DNS Proxy</title><style>:root{--bg:#ffffff;--text:#1d1d1f;--accent:#0071e3;--cf-orange:#f38020;--sub:#86868b;--card:#f5f5f7;--border:#d2d2d7;--code-bg:#f5f5f7}@media (prefers-color-scheme:dark){:root{--bg:#0b0e14;--text:#e6edf3;--accent:#2f81f7;--sub:#7d8590;--card:#161b22;--border:#30363d;--code-bg:#0d1117}}body{font-family:-apple-system,BlinkMacSystemFont,Inter,"Segoe UI",sans-serif;background:var(--bg);color:var(--text);margin:0;display:flex;align-items:center;justify-content:center;min-height:100vh;line-height:1.6;overflow-x:hidden;transition:background .3s,color .3s}.container{width:100%;max-width:850px;padding:40px 24px;box-sizing:border-box;animation:fadeIn .8s ease-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.header{text-align:left;margin-bottom:48px;border-bottom:1px solid var(--border);padding-bottom:24px}h1{font-size:2.8rem;margin:0;font-weight:800;letter-spacing:-.03em;background:linear-gradient(135deg,var(--text) 30%,var(--accent) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent}.tagline{color:var(--sub);font-size:1.1rem;margin-top:8px;font-weight:500}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:24px;margin-bottom:48px}.card{background:var(--card);border:1px solid var(--border);padding:28px;border-radius:12px;transition:all .3s cubic-bezier(.4,0,.2,1);cursor:default}.card:hover{border-color:var(--accent);transform:translateY(-4px);box-shadow:0 8px 24px rgba(0,0,0,.15)}.card h3{margin:0 0 12px 0;font-size:.85rem;text-transform:uppercase;letter-spacing:.08em;color:var(--accent);font-weight:700}.card p{margin:0;font-size:.95rem;color:var(--sub);font-weight:400;line-height:1.6}.endpoint-box{background:var(--code-bg);border:1px solid var(--border);padding:28px;border-radius:12px;text-align:left;margin-bottom:48px;transition:border-color .3s}.endpoint-box:hover{border-color:var(--sub)}.endpoint-label{display:block;font-size:.75rem;color:var(--sub);margin-bottom:12px;text-transform:uppercase;font-weight:700;letter-spacing:.05em}.code{font-family:SFMono-Regular,Consolas,"Liberation Mono",monospace;color:var(--accent);font-size:1.05rem;word-break:break-all}.links{display:flex;gap:32px;justify-content:flex-start}.links a{color:var(--text);text-decoration:none;font-size:.95rem;display:flex;align-items:center;font-weight:500;transition:color .2s}.links a:hover{color:var(--accent)}.links a svg{margin-right:8px;flex-shrink:0}.footer{margin-top:50px;text-align:left;font-size:.85rem;color:var(--sub);border-top:1px solid var(--border);padding-top:24px}.reveal-link{position:relative;text-decoration:none!important;transition:opacity .2s}.reveal-link::after{content:'';position:absolute;width:0;height:1.5px;bottom:-2px;left:50%;transform:translateX(-50%);transition:width .3s cubic-bezier(.4,0,.2,1)}.reveal-link:hover::after{width:100%}.author-link{color:inherit;font-weight:500}.author-link::after{background-color:var(--accent)}.author-link:hover{color:var(--accent);opacity:1}.cf-link{color:var(--cf-orange)!important;font-size:.8rem;font-weight:600;opacity:.9}.cf-link::after{background-color:var(--cf-orange)}.cf-link:hover{opacity:1}</style><div class="container"><header class="header"><h1>DoHflare</h1><div class="tagline">High-Performance DNS over HTTPS Edge Proxy</div></header><div class="grid"><div class="card"><h3>Multi-Tiered Caching Architecture</h3><p>Integrates high-speed L1 isolate memory caching with distributed L2 global caching, strictly enforcing TTL constraints to minimize upstream resolution latency.</div><div class="card"><h3>RFC Protocol Compliance</h3><p>Maintains absolute state transparency and preserves EDNS0 Client Subnet (ECS) payloads, ensuring optimal geolocation routing and DNS message integrity.</div><div class="card"><h3>Distributed System Resilience</h3><p>Utilizes collision-resistant FNV-1a hashing and deterministic TTL jitter algorithms to mitigate cache stampedes and sustain high availability under load.</div><div class="card"><h3>Edge-Level Traffic Management</h3><p>Implements concurrent request coalescing, aggressive payload size validation, and global write rate limiting to prevent upstream saturation and protocol abuse.</div></div><div class="endpoint-box"><span class="endpoint-label">Resolver Endpoint URL</span><div class="code">${fullServiceEndpoint}</div></div><div class="links"><a href="https://github.com/Racpast/DoHflare"target="_blank"><svg fill="currentColor"height="19"viewBox="0 0 16 16"width="19"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> GitHub </a><a href="https://www.gnu.org/licenses/agpl-3.0.html"target="_blank"><svg fill="currentColor"height="21"viewBox="0 0 1024 1024"width="21"><path d="M640 938.666667H256c-93.866667 0-170.666667-76.8-170.666667-170.666667v-42.666667c0-25.6 17.066667-42.666667 42.666667-42.666666h42.666667V256c0-93.866667 76.8-170.666667 170.666666-170.666667h469.333334c72.533333 0 128 55.466667 128 128s-55.466667 128-128 128h-42.666667v469.333334c0 72.533333-55.466667 128-128 128zm-384-256h298.666667c25.6 0 42.666667 17.066667 42.666666 42.666666v85.333334c0 25.6 17.066667 42.666667 42.666667 42.666666s42.666667-17.066667 42.666667-42.666666V213.333333c0-17.066667 4.266667-29.866667 8.533333-42.666666H341.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333v426.666667zm-85.333333 85.333333c0 46.933333 38.4 85.333333 85.333333 85.333333h264.533333c-4.266667-12.8-8.533333-25.6-8.533333-42.666666v-42.666667H170.666667zM768 256h42.666667c25.6 0 42.666667-17.066667 42.666666-42.666667s-17.066667-42.666667-42.666666-42.666666-42.666667 17.066667-42.666667 42.666666v42.666667zM554.666667 341.333333H384c-25.6 0-42.666667-17.066667-42.666667-42.666666s17.066667-42.666667 42.666667-42.666667h170.666667c25.6 0 42.666667 17.066667 42.666666 42.666667s-17.066667 42.666667-42.666666 42.666666zM554.666667 512H384c-25.6 0-42.666667-17.066667-42.666667-42.666667s17.066667-42.666667 42.666667-42.666666h170.666667c25.6 0 42.666667 17.066667 42.666666 42.666666s-17.066667 42.666667-42.666666 42.666667"/></svg> License: AGPL-3.0</a></div><footer class="footer">Copyright © ${currentYear} <a href="https://github.com/Racpast"target="_blank"class="reveal-link author-link">Racpast</a>. All rights reserved.<br><span style="opacity:.8;margin-top:10px;display:block">Powered by <a href="https://workers.cloudflare.com/"target="_blank"class="reveal-link cf-link">Cloudflare Workers</a></span></footer></div>`;
  }

  static async processIncomingRequest(
    incomingRequest,
    environmentVariables,
    executionContext,
  ) {
    const requestConfig = this.initializeConfig(environmentVariables);
    const parsedUrl = new URL(incomingRequest.url);

    if (incomingRequest.method === "OPTIONS") {
      return new Response(null, {
        status: 204,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
          "Access-Control-Allow-Headers": "Content-Type, Accept",
          "Access-Control-Max-Age": "86400",
        },
      });
    }
    if (parsedUrl.pathname !== requestConfig.DOH_PATH) {
      if (parsedUrl.pathname === "/") {
        let responseContent;
        let responseType;

        if (
          requestConfig.ROOT_CONTENT &&
          requestConfig.ROOT_CONTENT.trim() !== ""
        ) {
          responseContent = requestConfig.ROOT_CONTENT;
          responseType = requestConfig.ROOT_CONTENT_TYPE;
        } else {
          responseContent = this.generateLandingPage(
            incomingRequest,
            requestConfig,
          );
          responseType = "text/html; charset=utf-8";
        }

        return new Response(responseContent, {
          status: 200,
          headers: {
            "Content-Type": responseType,
            "Cache-Control": `public, max-age=${requestConfig.ROOT_CACHE_TTL}`,
            "X-DOHFLARE-Mode": requestConfig.ROOT_CONTENT
              ? "Custom"
              : "Template",
          },
        });
      }
      return new Response("Not Found", { status: 404 });
    }

    let rawQueryBuffer;
    if (incomingRequest.method === "GET") {
      const dnsQueryParam = parsedUrl.searchParams.get("dns");
      if (!dnsQueryParam)
        return new Response("Bad Request: missing dns", { status: 400 });
      try {
        rawQueryBuffer = convertBase64UrlToBuffer(dnsQueryParam);
      } catch {
        return new Response("Bad Request: invalid base64url", { status: 400 });
      }
    } else if (incomingRequest.method === "POST") {
      if (
        !(incomingRequest.headers.get("content-type") || "").includes(
          requestConfig.DOH_CONTENT_TYPE,
        )
      )
        return new Response("Unsupported Media Type", { status: 415 });
      try {
        rawQueryBuffer = await incomingRequest.arrayBuffer();
        if (rawQueryBuffer.byteLength > requestConfig.MAX_POST_BODY_BYTES)
          return new Response("Payload Too Large", { status: 413 });
      } catch {
        return new Response("Bad Request", { status: 400 });
      }
    } else {
      return new Response("Method Not Allowed", { status: 405 });
    }

    if (rawQueryBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN)
      return new Response("Malformed DNS Query", { status: 400 });

    const transactionIdByteArray = new Uint8Array(rawQueryBuffer.slice(0, 2));
    const resolvedClientIp = normalizeClientIp(
      incomingRequest.headers.get("cf-connecting-ip"),
      requestConfig.FALLBACK_ECS_IP,
    );
    const hasClientEcs =
      DnsPacketProcessor.verifyEcsOptionPresence(rawQueryBuffer);
    const processedQueryBuffer = DnsPacketProcessor.injectEcsPayload(
      rawQueryBuffer,
      resolvedClientIp,
      requestConfig.FALLBACK_ECS_IP,
    );
    const cacheKeyHex =
      DnsPacketProcessor.generateStableCacheKey(processedQueryBuffer);
    const probabilitySample = parseInt(cacheKeyHex.slice(-4), 16) % 10000;
    const currentTimestamp = Date.now();

    const memoryCacheEntry = primaryMemoryCache.get(
      cacheKeyHex,
      requestConfig.EXTREME_STALE_FALLBACK_MS,
    );
    if (memoryCacheEntry) {
      if (!memoryCacheEntry.hitTimestamps) {
        memoryCacheEntry.hitTimestamps = new Float64Array(
          requestConfig.HOT_HIT_THRESHOLD,
        );
        memoryCacheEntry.currentHitIndex = 0;
        memoryCacheEntry.totalHitCount = 0;
      }
      memoryCacheEntry.hitTimestamps[memoryCacheEntry.currentHitIndex] =
        currentTimestamp;
      memoryCacheEntry.currentHitIndex =
        (memoryCacheEntry.currentHitIndex + 1) %
        requestConfig.HOT_HIT_THRESHOLD;
      if (memoryCacheEntry.totalHitCount < requestConfig.HOT_HIT_THRESHOLD)
        memoryCacheEntry.totalHitCount++;

      if (currentTimestamp < memoryCacheEntry.expiryTimestamp) {
        const cacheAgeSeconds = Math.max(
          0,
          Math.floor(
            (memoryCacheEntry.expiryTimestamp - currentTimestamp) / 1000,
          ),
        );
        return this.createResponse(
          memoryCacheEntry.payloadByteArray,
          transactionIdByteArray,
          hasClientEcs,
          cacheAgeSeconds,
          "HIT-L1",
          cacheKeyHex,
          requestConfig,
        );
      }

      const staleWindowMilliseconds = Math.floor(
        (memoryCacheEntry.expiryTimestamp -
          (memoryCacheEntry.storedTimestamp ||
            memoryCacheEntry.expiryTimestamp)) *
          requestConfig.STALE_WINDOW_FACTOR,
      );
      if (
        currentTimestamp <
        memoryCacheEntry.expiryTimestamp + staleWindowMilliseconds
      ) {
        if (!requestCoalescingMap.has(cacheKeyHex)) {
          executionContext.waitUntil(
            this.resolveUpstream(
              processedQueryBuffer,
              cacheKeyHex,
              probabilitySample,
              true,
              null,
              null,
              requestConfig,
            ).catch(() => {}),
          );
        }
        return this.createResponse(
          memoryCacheEntry.payloadByteArray,
          transactionIdByteArray,
          hasClientEcs,
          0,
          "HIT-L1-STALE",
          cacheKeyHex,
          requestConfig,
        );
      }
    }

    const globalCacheRequestUrl = new Request(
      `${requestConfig.GLOBAL_CACHE_NAMESPACE}${cacheKeyHex}`,
    );
    if (probabilitySample < requestConfig.CF_CACHE_WRITE_THRESHOLD) {
      try {
        const globalCacheResponse = await caches.default.match(
          globalCacheRequestUrl,
        );
        if (globalCacheResponse && globalCacheResponse.ok) {
          const responseArrayBuffer = await globalCacheResponse.arrayBuffer();
          if (
            responseArrayBuffer.byteLength <= requestConfig.MAX_CACHEABLE_BYTES
          ) {
            const payloadByteArray = new Uint8Array(responseArrayBuffer);
            const ttlFromHeader = Number(
              globalCacheResponse.headers.get("X-DOHFLARE-Original-TTL") ||
                requestConfig.DEFAULT_POSITIVE_TTL,
            );
            primaryMemoryCache.set(
              cacheKeyHex,
              {
                payloadByteArray: payloadByteArray,
                expiryTimestamp: Date.now() + ttlFromHeader * 1000,
                payloadSize: payloadByteArray.byteLength,
                lastGlobalWriteTimestamp: Date.now(),
                storedTimestamp: Date.now(),
              },
              requestConfig.EXTREME_STALE_FALLBACK_MS,
            );
            return this.createResponse(
              payloadByteArray,
              transactionIdByteArray,
              hasClientEcs,
              ttlFromHeader,
              "HIT-L2-GLOBAL",
              cacheKeyHex,
              requestConfig,
            );
          }
        }
      } catch (error) {
        /* Background degradation handling */
      }
    }

    if (requestCoalescingMap.has(cacheKeyHex)) {
      try {
        const coalescedResult = await requestCoalescingMap.get(cacheKeyHex);
        return this.createResponse(
          coalescedResult.payloadByteArray,
          transactionIdByteArray,
          hasClientEcs,
          coalescedResult.jitteredTtl,
          "MISS-COALESCED",
          cacheKeyHex,
          requestConfig,
          coalescedResult.httpStatus,
        );
      } catch (error) {
        /* Coalescing fallback triggers internal miss */
      }
    }

    let promiseResolver = (value) => {};
    let promiseRejecter = (reason) => {};
    const upstreamFetchPromise = new Promise((resolve, reject) => {
      promiseResolver = resolve;
      promiseRejecter = reject;
    });
    requestCoalescingMap.set(cacheKeyHex, upstreamFetchPromise);

    try {
      const resolutionResult = await this.resolveUpstream(
        processedQueryBuffer,
        cacheKeyHex,
        probabilitySample,
        false,
        executionContext,
        globalCacheRequestUrl,
        requestConfig,
      );
      promiseResolver(resolutionResult);
      return this.createResponse(
        resolutionResult.payloadByteArray,
        transactionIdByteArray,
        hasClientEcs,
        resolutionResult.jitteredTtl,
        "MISS",
        cacheKeyHex,
        requestConfig,
        resolutionResult.httpStatus || 200,
        {
          "X-DOHFLARE-Write-Decision": resolutionResult.cacheWriteDecision,
          "X-DOHFLARE-RCODE": String(resolutionResult.responseCode || 0),
        },
      );
    } catch (resolutionError) {
      promiseRejecter(resolutionError);
      const staleCacheEntry = primaryMemoryCache.peek(cacheKeyHex);
      if (staleCacheEntry && staleCacheEntry.payloadByteArray) {
        return this.createResponse(
          staleCacheEntry.payloadByteArray,
          transactionIdByteArray,
          hasClientEcs,
          0,
          "HIT-L1-STALE-FALLBACK",
          cacheKeyHex,
          requestConfig,
          200,
          { "X-DOHFLARE-Degraded": "1" },
        );
      }
      return new Response("Bad Gateway", {
        status: 502,
        headers: { "X-DOHFLARE-Code": "UPSTREAM_ERR" },
      });
    } finally {
      requestCoalescingMap.delete(cacheKeyHex);
    }
  }

  static async resolveUpstream(
    processedQueryBuffer,
    cacheKeyHex,
    probabilitySample,
    isBackgroundExecution,
    executionContext,
    globalCacheRequestUrl,
    requestConfig,
  ) {
    let internalResolutionError = null;
    const maxAttempts = requestConfig.MAX_RETRIES + 1;
    const urlsCount = requestConfig.UPSTREAM_URLS.length;

    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      const targetUrl = requestConfig.UPSTREAM_URLS[attempt % urlsCount];

      try {
        return await this.fetchUpstream(
          targetUrl,
          processedQueryBuffer,
          cacheKeyHex,
          probabilitySample,
          isBackgroundExecution,
          executionContext,
          globalCacheRequestUrl,
          requestConfig,
        );
      } catch (error) {
        internalResolutionError = error;
      }
    }
    throw internalResolutionError || new Error(ERRORS.TIMEOUT);
  }

  static async fetchUpstream(
    upstreamUrlEndpoint,
    processedQueryBuffer,
    cacheKeyHex,
    probabilitySample,
    isBackgroundExecution,
    executionContext,
    globalCacheRequestUrl,
    requestConfig,
  ) {
    const upstreamResponse = await executeFetchWithTimeout(
      upstreamUrlEndpoint,
      {
        method: "POST",
        headers: {
          "Content-Type": requestConfig.DOH_CONTENT_TYPE,
          Accept: requestConfig.DOH_CONTENT_TYPE,
        },
        body: processedQueryBuffer,
      },
      requestConfig.FETCH_TIMEOUT_MS,
    );
    if (!upstreamResponse.ok)
      throw new Error(`Upstream HTTP ${upstreamResponse.status}`);
    const responseArrayBuffer = await upstreamResponse.arrayBuffer();
    if (responseArrayBuffer.byteLength > requestConfig.MAX_CACHEABLE_BYTES)
      throw new Error(ERRORS.OVERSIZED);

    const payloadByteArray = new Uint8Array(responseArrayBuffer);
    const { extractedTtl, responseCode } =
      DnsPacketProcessor.extractTtlAndResponseCode(
        responseArrayBuffer,
        requestConfig.DEFAULT_POSITIVE_TTL,
      );
    const effectiveTtlValue =
      responseCode === DNS_CONSTANTS.RCODE_NXDOMAIN
        ? requestConfig.DEFAULT_NEGATIVE_TTL
        : extractedTtl;
    const jitteredTtl = Math.max(
      1,
      Math.floor(
        effectiveTtlValue *
          this.calculateDeterministicJitter(
            cacheKeyHex,
            requestConfig.JITTER_PCT,
          ),
      ),
    );
    primaryMemoryCache.set(
      cacheKeyHex,
      {
        payloadByteArray: payloadByteArray,
        expiryTimestamp: Date.now() + jitteredTtl * 1000,
        payloadSize: payloadByteArray.byteLength,
        storedTimestamp: Date.now(),
      },
      requestConfig.EXTREME_STALE_FALLBACK_MS,
    );

    let cacheWriteDecision = "SKIP";
    if (!isBackgroundExecution && executionContext && globalCacheRequestUrl) {
      const existingCacheEntry = primaryMemoryCache.peek(cacheKeyHex);
      let forceHotWrite = false;
      if (
        existingCacheEntry &&
        existingCacheEntry.totalHitCount === requestConfig.HOT_HIT_THRESHOLD
      ) {
        if (
          Date.now() -
            existingCacheEntry.hitTimestamps[
              existingCacheEntry.currentHitIndex
            ] <=
          requestConfig.HOT_WINDOW_MS
        )
          forceHotWrite = true;
      }

      if (
        probabilitySample < requestConfig.CF_CACHE_WRITE_THRESHOLD ||
        forceHotWrite
      ) {
        const lastGlobalWriteTimestamp = existingCacheEntry
          ? existingCacheEntry.lastGlobalWriteTimestamp || 0
          : 0;
        if (
          Date.now() - lastGlobalWriteTimestamp >
            requestConfig.GLOBAL_WRITE_COOLDOWN_MS &&
          this.checkWriteRateLimit(requestConfig)
        ) {
          if (!activeGlobalWriteLocks.has(cacheKeyHex)) {
            activeGlobalWriteLocks.add(cacheKeyHex);
            try {
              const globalMatchResult = await caches.default.match(
                globalCacheRequestUrl,
              );
              if (!globalMatchResult) {
                const globalCacheHeaders = new Headers({
                  "Content-Type": requestConfig.DOH_CONTENT_TYPE,
                  "Cache-Control": `public, max-age=${jitteredTtl}`,
                  "X-DOHFLARE-Original-TTL": String(jitteredTtl),
                });
                if (existingCacheEntry)
                  existingCacheEntry.lastGlobalWriteTimestamp = Date.now();
                executionContext.waitUntil(
                  caches.default
                    .put(
                      globalCacheRequestUrl,
                      new Response(payloadByteArray.slice(0), {
                        status: 200,
                        headers: globalCacheHeaders,
                      }),
                    )
                    .catch(() => {}),
                );
                cacheWriteDecision = forceHotWrite
                  ? "WRITE-FORCE"
                  : "WRITE-SCHEDULED";
              } else {
                cacheWriteDecision = "ALREADY-PRESENT";
                if (existingCacheEntry)
                  existingCacheEntry.lastGlobalWriteTimestamp = Date.now();
              }
            } catch {
              cacheWriteDecision = "GLOBAL-CHECK-ERR";
            } finally {
              activeGlobalWriteLocks.delete(cacheKeyHex);
            }
          } else {
            cacheWriteDecision = "WRITE-LOCKED";
          }
        } else {
          cacheWriteDecision = "COOLDOWN-OR-RATE-LIMIT";
        }
      }
    }
    return {
      payloadByteArray,
      httpStatus: upstreamResponse.status,
      jitteredTtl,
      cacheWriteDecision,
      responseCode,
    };
  }
}

/* ==========================================================================
   7. WORKER ENTRY POINT
   ========================================================================== */
export default {
  async fetch(incomingRequest, environmentVariables, executionContext) {
    try {
      return await DoHRequestHandler.processIncomingRequest(
        incomingRequest,
        environmentVariables,
        executionContext,
      );
    } catch (criticalError) {
      return new Response("Internal Server Error", {
        status: 500,
        headers: { "X-DOHFLARE-Code": "INTERNAL_FATAL" },
      });
    }
  },
};

最后更新于: