Skip to content

x-cache-key header crashes when cache key contains non-ASCII characters #69

Description

@aui

Package: @web-widget/shared-cache
Version: 1.8.0
Environment: Node.js (undici Headers.set)

Summary

When debugCacheKey is enabled, createSharedCacheFetch writes the computed cache key to the x-cache-key response header. If the cache key contains Unicode characters (e.g. from url.pathname or query values), Headers.set throws because HTTP header values must be a ByteString (code points ≤ 255).

Steps to reproduce

  1. Enable debugCacheKey: true on the cache middleware or fetch wrapper.
  2. Use default cache key rules with pathname: true (and/or search: true).
  3. Request a URL whose pathname or search params contain non-ASCII characters after URL decoding, e.g. /templates/海报设计.

Actual behavior

TypeError: Cannot convert argument to a ByteString because the character at index 46 has a value of 21830 which is greater than 255.
    at webidl.converters.ByteString (node:internal/deps/undici/undici:3889:17)
    at _Headers.set (node:internal/deps/undici/undici:8921:35)
    at setResponseHeader (shared-cache.js)
    at setCacheKey (shared-cache.js)
    at fetch (shared-cache.js)

The request fails instead of returning a cached or fresh response.

Expected behavior

debugCacheKey should not crash when the cache key contains Unicode. The header value should be encoded to a ByteString-safe form before calling Headers.set.

Root cause

Cache keys are built from raw url.pathname / search values, which JavaScript decodes to Unicode. setCacheKey passes the string directly to headers.set('x-cache-key', cacheKey) without encoding.

Relevant code path:

  • createSharedCacheFetchsetCacheKeysetResponseHeaderheaders.set(CACHE_KEY_HEADER_NAME, cacheKey)

Suggested fix

Encode the header value when it contains non-Latin-1 characters, for example:

function toCacheKeyHeaderValue(cacheKey) {
  for (let i = 0; i < cacheKey.length; i++) {
    if (cacheKey.charCodeAt(i) > 255) {
      return encodeURIComponent(cacheKey);
    }
  }
  return cacheKey;
}

function setCacheKey(response, cacheKey) {
  if (cacheKey) {
    return setResponseHeader(
      response,
      CACHE_KEY_HEADER_NAME,
      toCacheKeyHeaderValue(cacheKey),
    );
  }
  return response;
}

Consider documenting in README that x-cache-key may be encodeURIComponent-encoded when the raw key is not ByteString-safe. Consumers can recover the original key with decodeURIComponent.

Alternative: always use Base64URL encoding with a prefix (e.g. b64:) for a more explicit contract.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions