Skip to content

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

@aui

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions