Repository: nodejs/undici

Problem statement:
sql cache should always have a entry size limit
We can't store more than 2GB.


Hints:

Patch:
diff --git a/eslint.config.js b/eslint.config.js
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -7,6 +7,7 @@ module.exports = [
     ignores: [
       'lib/llhttp',
       'test/fixtures/wpt',
+      'test/fixtures/cache-tests',
       'undici-fetch.js'
     ],
     noJsx: true,
diff --git a/lib/cache/memory-cache-store.js b/lib/cache/memory-cache-store.js
--- a/lib/cache/memory-cache-store.js
+++ b/lib/cache/memory-cache-store.js
@@ -89,6 +89,7 @@ class MemoryCacheStore {
           statusCode: entry.statusCode,
           headers: entry.headers,
           body: entry.body,
+          vary: entry.vary ? entry.vary : undefined,
           etag: entry.etag,
           cacheControlDirectives: entry.cacheControlDirectives,
           cachedAt: entry.cachedAt,
diff --git a/lib/cache/sqlite-cache-store.js b/lib/cache/sqlite-cache-store.js
--- a/lib/cache/sqlite-cache-store.js
+++ b/lib/cache/sqlite-cache-store.js
@@ -6,6 +6,9 @@ const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
 
 const VERSION = 3
 
+// 2gb
+const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
+
 /**
  * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
  * @implements {CacheStore}
@@ -18,7 +21,7 @@ const VERSION = 3
  * } & import('../../types/cache-interceptor.d.ts').default.CacheValue} SqliteStoreValue
  */
 module.exports = class SqliteCacheStore {
-  #maxEntrySize = Infinity
+  #maxEntrySize = MAX_ENTRY_SIZE
   #maxCount = Infinity
 
   /**
@@ -78,6 +81,11 @@ module.exports = class SqliteCacheStore {
         ) {
           throw new TypeError('SqliteCacheStore options.maxEntrySize must be a non-negative integer')
         }
+
+        if (opts.maxEntrySize > MAX_ENTRY_SIZE) {
+          throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')
+        }
+
         this.#maxEntrySize = opts.maxEntrySize
       }
 
@@ -227,6 +235,7 @@ module.exports = class SqliteCacheStore {
       statusMessage: value.statusMessage,
       headers: value.headers ? JSON.parse(value.headers) : undefined,
       etag: value.etag ? value.etag : undefined,
+      vary: value.vary ?? undefined,
       cacheControlDirectives: value.cacheControlDirectives
         ? JSON.parse(value.cacheControlDirectives)
         : undefined,
@@ -394,10 +403,10 @@ module.exports = class SqliteCacheStore {
           return undefined
         }
 
-        const vary = JSON.parse(value.vary)
+        value.vary = JSON.parse(value.vary)
 
-        for (const header in vary) {
-          if (!headerValueEquals(headers[header], vary[header])) {
+        for (const header in value.vary) {
+          if (!headerValueEquals(headers[header], value.vary[header])) {
             matches = false
             break
           }
diff --git a/lib/handler/cache-handler.js b/lib/handler/cache-handler.js
--- a/lib/handler/cache-handler.js
+++ b/lib/handler/cache-handler.js
@@ -10,7 +10,9 @@ const {
 function noop () {}
 
 /**
- * @implements {import('../../types/dispatcher.d.ts').default.DispatchHandler}
+ * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler
+ *
+ * @implements {DispatchHandler}
  */
 class CacheHandler {
   /**
@@ -18,6 +20,16 @@ class CacheHandler {
    */
   #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}
    */
@@ -38,8 +50,10 @@ class CacheHandler {
    * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
    * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
    */
-  constructor ({ store }, cacheKey, handler) {
+  constructor ({ store, type, cacheByDefault }, cacheKey, handler) {
     this.#store = store
+    this.#cacheType = type
+    this.#cacheByDefault = cacheByDefault
     this.#cacheKey = cacheKey
     this.#handler = handler
   }
@@ -83,24 +97,47 @@ class CacheHandler {
     }
 
     const cacheControlHeader = headers['cache-control']
-    if (!cacheControlHeader) {
+    if (!cacheControlHeader && !headers['expires'] && !this.#cacheByDefault) {
       // Don't have the cache control header or the cache is full
       return downstreamOnHeaders()
     }
 
-    const cacheControlDirectives = parseCacheControlHeader(cacheControlHeader)
-    if (!canCacheResponse(statusCode, headers, cacheControlDirectives)) {
+    const cacheControlDirectives = cacheControlHeader ? parseCacheControlHeader(cacheControlHeader) : {}
+    if (!canCacheResponse(this.#cacheType, statusCode, headers, cacheControlDirectives)) {
       return downstreamOnHeaders()
     }
 
+    const age = getAge(headers)
+
     const now = Date.now()
-    const staleAt = determineStaleAt(now, headers, cacheControlDirectives)
+    const staleAt = determineStaleAt(this.#cacheType, now, headers, cacheControlDirectives) ?? this.#cacheByDefault
     if (staleAt) {
-      const varyDirectives = this.#cacheKey.headers && headers.vary
-        ? parseVaryHeader(headers.vary, this.#cacheKey.headers)
-        : undefined
-      const deleteAt = determineDeleteAt(now, cacheControlDirectives, staleAt)
+      let baseTime = now
+      if (headers['date']) {
+        const parsedDate = parseInt(headers['date'])
+        const date = new Date(isNaN(parsedDate) ? headers['date'] : parsedDate)
+        if (date instanceof Date && !isNaN(date)) {
+          baseTime = date.getTime()
+        }
+      }
+
+      const absoluteStaleAt = staleAt + baseTime
+
+      if (now >= absoluteStaleAt || (age && age >= staleAt)) {
+        // Response is already stale
+        return downstreamOnHeaders()
+      }
+
+      let varyDirectives
+      if (this.#cacheKey.headers && headers.vary) {
+        varyDirectives = parseVaryHeader(headers.vary, this.#cacheKey.headers)
+        if (!varyDirectives) {
+          // Parse error
+          return downstreamOnHeaders()
+        }
+      }
 
+      const deleteAt = determineDeleteAt(cacheControlDirectives, absoluteStaleAt)
       const strippedHeaders = stripNecessaryHeaders(headers, cacheControlDirectives)
 
       /**
@@ -112,8 +149,8 @@ class CacheHandler {
         headers: strippedHeaders,
         vary: varyDirectives,
         cacheControlDirectives,
-        cachedAt: now,
-        staleAt,
+        cachedAt: age ? now - (age * 1000) : now,
+        staleAt: absoluteStaleAt,
         deleteAt
       }
 
@@ -129,6 +166,7 @@ class CacheHandler {
           .on('drain', () => controller.resume())
           .on('error', function () {
             // TODO (fix): Make error somehow observable?
+            handler.#writeStream = undefined
           })
           .on('close', function () {
             if (handler.#writeStream === this) {
@@ -167,25 +205,29 @@ class CacheHandler {
 /**
  * @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 {Record<string, string | string[]>} headers
  * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
  */
-function canCacheResponse (statusCode, headers, cacheControlDirectives) {
+function canCacheResponse (cacheType, statusCode, headers, cacheControlDirectives) {
   if (statusCode !== 200 && statusCode !== 307) {
     return false
   }
 
   if (
-    cacheControlDirectives.private === true ||
     cacheControlDirectives['no-cache'] === true ||
     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 (headers.vary === '*') {
+  if (headers.vary?.includes('*')) {
     return false
   }
 
@@ -214,60 +256,88 @@ function canCacheResponse (statusCode, headers, cacheControlDirectives) {
 }
 
 /**
+ * @param {Record<string, string | string[]>} headers
+ * @returns {number | undefined}
+ */
+function getAge (headers) {
+  if (!headers.age) {
+    return undefined
+  }
+
+  const age = parseInt(Array.isArray(headers.age) ? headers.age[0] : headers.age)
+  if (isNaN(age) || age >= 2147483647) {
+    return undefined
+  }
+
+  return age
+}
+
+/**
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
  * @param {number} now
  * @param {Record<string, string | string[]>} headers
  * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
  *
  * @returns {number | undefined} time that the value is stale at or undefined if it shouldn't be cached
  */
-function determineStaleAt (now, headers, cacheControlDirectives) {
-  // 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) {
-    return now + (sMaxAge * 1000)
-  }
-
-  if (cacheControlDirectives.immutable) {
-    // https://www.rfc-editor.org/rfc/rfc8246.html#section-2.2
-    return now + 31536000
+function determineStaleAt (cacheType, now, headers, 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) {
+      return sMaxAge * 1000
+    }
   }
 
   const maxAge = cacheControlDirectives['max-age']
   if (maxAge) {
-    return now + (maxAge * 1000)
+    return maxAge * 1000
   }
 
-  if (headers.expire && typeof headers.expire === 'string') {
+  if (headers.expires && typeof headers.expires === 'string') {
     // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
-    const expiresDate = new Date(headers.expire)
+    const expiresDate = new Date(headers.expires)
     if (expiresDate instanceof Date && Number.isFinite(expiresDate.valueOf())) {
-      return now + (Date.now() - expiresDate.getTime())
+      if (now >= expiresDate.getTime()) {
+        return undefined
+      }
+
+      return expiresDate.getTime() - now
     }
   }
 
+  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) {
+function determineDeleteAt (cacheControlDirectives, staleAt) {
   let staleWhileRevalidate = -Infinity
   let staleIfError = -Infinity
+  let immutable = -Infinity
 
   if (cacheControlDirectives['stale-while-revalidate']) {
-    staleWhileRevalidate = now + (cacheControlDirectives['stale-while-revalidate'] * 1000)
+    staleWhileRevalidate = staleAt + (cacheControlDirectives['stale-while-revalidate'] * 1000)
   }
 
   if (cacheControlDirectives['stale-if-error']) {
-    staleIfError = now + (cacheControlDirectives['stale-if-error'] * 1000)
+    staleIfError = staleAt + (cacheControlDirectives['stale-if-error'] * 1000)
   }
 
-  return Math.max(staleAt, staleWhileRevalidate, staleIfError)
+  if (staleWhileRevalidate === -Infinity && staleIfError === -Infinity) {
+    immutable = 31536000
+  }
+
+  return Math.max(staleAt, staleWhileRevalidate, staleIfError, immutable)
 }
 
 /**
@@ -277,7 +347,29 @@ function determineDeleteAt (now, cacheControlDirectives, staleAt) {
  * @returns {Record<string, string | string []>}
  */
 function stripNecessaryHeaders (headers, cacheControlDirectives) {
-  const headersToRemove = ['connection']
+  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 (headers['connection']) {
+    if (Array.isArray(headers['connection'])) {
+      // connection: a
+      // connection: b
+      headersToRemove.push(...headers['connection'].map(header => header.trim()))
+    } else {
+      // connection: a, b
+      headersToRemove.push(...headers['connection'].split(',').map(header => header.trim()))
+    }
+  }
 
   if (Array.isArray(cacheControlDirectives['no-cache'])) {
     headersToRemove.push(...cacheControlDirectives['no-cache'])
@@ -288,12 +380,13 @@ function stripNecessaryHeaders (headers, cacheControlDirectives) {
   }
 
   let strippedHeaders
-  for (const headerName of Object.keys(headers)) {
-    if (headersToRemove.includes(headerName)) {
+  for (const headerName of headersToRemove) {
+    if (headers[headerName]) {
       strippedHeaders ??= { ...headers }
-      delete headers[headerName]
+      delete strippedHeaders[headerName]
     }
   }
+
   return strippedHeaders ?? headers
 }
 
diff --git a/lib/interceptor/cache.js b/lib/interceptor/cache.js
--- a/lib/interceptor/cache.js
+++ b/lib/interceptor/cache.js
@@ -1,4 +1,3 @@
-// @ts-check
 'use strict'
 
 const assert = require('node:assert')
@@ -196,7 +195,6 @@ function handleResult (
   if (!result) {
     return handleUncachedResponse(dispatch, globalOpts, cacheKey, handler, opts, reqCacheControl)
   }
-
   if (!result.body && opts.method !== 'HEAD') {
     throw new Error('body is undefined but method isn\'t HEAD')
   }
@@ -228,15 +226,24 @@ function handleResult (
       withinStaleIfErrorThreshold = now < (result.staleAt + (staleIfErrorExpiry * 1000))
     }
 
+    let headers = {
+      ...opts.headers,
+      'if-modified-since': new Date(result.cachedAt).toUTCString(),
+      'if-none-match': result.etag
+    }
+
+    if (result.vary) {
+      headers = {
+        ...headers,
+        ...result.vary
+      }
+    }
+
     // We need to revalidate the response
     return dispatch(
       {
         ...opts,
-        headers: {
-          ...opts.headers,
-          'if-modified-since': new Date(result.cachedAt).toUTCString(),
-          'if-none-match': result.etag
-        }
+        headers
       },
       new CacheRevalidationHandler(
         (success, context) => {
@@ -267,7 +274,9 @@ function handleResult (
 module.exports = (opts = {}) => {
   const {
     store = new MemoryCacheStore(),
-    methods = ['GET']
+    methods = ['GET'],
+    cacheByDefault = undefined,
+    type = 'shared'
   } = opts
 
   if (typeof opts !== 'object' || opts === null) {
@@ -277,9 +286,19 @@ module.exports = (opts = {}) => {
   assertCacheStore(store, 'opts.store')
   assertCacheMethods(methods, 'opts.methods')
 
+  if (typeof cacheByDefault !== 'undefined' && typeof cacheByDefault !== 'number') {
+    throw new TypeError(`exepcted opts.cacheByDefault to be number or undefined, got ${typeof cacheByDefault}`)
+  }
+
+  if (typeof type !== 'undefined' && type !== 'shared' && type !== 'private') {
+    throw new TypeError(`exepcted opts.type to be shared, private, or undefined, got ${typeof type}`)
+  }
+
   const globalOpts = {
     store,
-    methods
+    methods,
+    cacheByDefault,
+    type
   }
 
   const safeMethodsToNotCache = util.safeHTTPMethods.filter(method => methods.includes(method) === false)
diff --git a/lib/util/cache.js b/lib/util/cache.js
--- a/lib/util/cache.js
+++ b/lib/util/cache.js
@@ -106,7 +106,17 @@ function parseCacheControlHeader (header) {
    */
   const output = {}
 
-  const directives = Array.isArray(header) ? header : header.split(',')
+  let directives
+  if (Array.isArray(header)) {
+    directives = []
+
+    for (const directive of header) {
+      directives.push(...directive.split(','))
+    }
+  } else {
+    directives = header.split(',')
+  }
+
   for (let i = 0; i < directives.length; i++) {
     const directive = directives[i].toLowerCase()
     const keyValueDelimiter = directive.indexOf('=')
@@ -114,10 +124,8 @@ function parseCacheControlHeader (header) {
     let key
     let value
     if (keyValueDelimiter !== -1) {
-      key = directive.substring(0, keyValueDelimiter).trim()
-      value = directive
-        .substring(keyValueDelimiter + 1)
-        .trim()
+      key = directive.substring(0, keyValueDelimiter).trimStart()
+      value = directive.substring(keyValueDelimiter + 1)
     } else {
       key = directive.trim()
     }
@@ -129,16 +137,28 @@ function parseCacheControlHeader (header) {
       case 's-maxage':
       case 'stale-while-revalidate':
       case 'stale-if-error': {
-        if (value === undefined) {
+        if (value === undefined || value[0] === ' ') {
           continue
         }
 
+        if (
+          value.length >= 2 &&
+          value[0] === '"' &&
+          value[value.length - 1] === '"'
+        ) {
+          value = value.substring(1, value.length - 1)
+        }
+
         const parsedValue = parseInt(value, 10)
         // eslint-disable-next-line no-self-compare
         if (parsedValue !== parsedValue) {
           continue
         }
 
+        if (key === 'max-age' && key in output && output[key] >= parsedValue) {
+          continue
+        }
+
         output[key] = parsedValue
 
         break
@@ -187,11 +207,19 @@ function parseCacheControlHeader (header) {
                 headers[headers.length - 1] = lastHeader
               }
 
-              output[key] = headers
+              if (key in output) {
+                output[key] = output[key].concat(headers)
+              } else {
+                output[key] = headers
+              }
             }
           } else {
             // Something like `no-cache=some-header`
-            output[key] = [value]
+            if (key in output) {
+              output[key] = output[key].concat(value)
+            } else {
+              output[key] = [value]
+            }
           }
 
           break
@@ -229,7 +257,7 @@ function parseCacheControlHeader (header) {
  * @returns {Record<string, string | string[]>}
  */
 function parseVaryHeader (varyHeader, headers) {
-  if (typeof varyHeader === 'string' && varyHeader === '*') {
+  if (typeof varyHeader === 'string' && varyHeader.includes('*')) {
     return headers
   }
 
@@ -243,6 +271,8 @@ function parseVaryHeader (varyHeader, headers) {
 
     if (headers[trimmedHeader]) {
       output[trimmedHeader] = headers[trimmedHeader]
+    } else {
+      return undefined
     }
   }
 
diff --git a/types/cache-interceptor.d.ts b/types/cache-interceptor.d.ts
--- a/types/cache-interceptor.d.ts
+++ b/types/cache-interceptor.d.ts
@@ -7,6 +7,10 @@ declare namespace CacheHandler {
 
   export interface CacheHandlerOptions {
     store: CacheStore
+
+    cacheByDefault?: number
+
+    type?: CacheOptions['type']
   }
 
   export interface CacheOptions {
@@ -20,6 +24,20 @@ declare namespace CacheHandler {
      * @see https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1
      */
     methods?: CacheMethods[]
+
+    /**
+     * RFC9111 allows for caching responses that we aren't explicitly told to
+     *  cache or to not cache.
+     * @see https://www.rfc-editor.org/rfc/rfc9111.html#section-3-5
+     * @default undefined
+     */
+    cacheByDefault?: number
+
+    /**
+     * TODO docs
+     * @default 'shared'
+     */
+    type?: 'shared' | 'private'
   }
 
   export interface CacheControlDirectives {
@@ -70,6 +88,7 @@ declare namespace CacheHandler {
     statusCode: number
     statusMessage: string
     headers: Record<string, string | string[]>
+    vary?: Record<string, string | string[]>
     etag?: string
     body: null | Readable | Iterable<Buffer> | AsyncIterable<Buffer> | Buffer | Iterable<string> | AsyncIterable<string> | string
     cacheControlDirectives: CacheControlDirectives,


Test patch:
diff --git a/test/cache-interceptor/cache-store-test-utils.js b/test/cache-interceptor/cache-store-test-utils.js
--- a/test/cache-interceptor/cache-store-test-utils.js
+++ b/test/cache-interceptor/cache-store-test-utils.js
@@ -58,6 +58,7 @@ function cacheStoreTests (CacheStore) {
       deepStrictEqual(await readResponse(readResult), {
         ...requestValue,
         etag: undefined,
+        vary: undefined,
         cacheControlDirectives: {},
         body: Buffer.concat(requestBody.map(x => Buffer.from(x)))
       })
@@ -97,6 +98,7 @@ function cacheStoreTests (CacheStore) {
       deepStrictEqual(await readResponse(readResult), {
         ...anotherValue,
         etag: undefined,
+        vary: undefined,
         cacheControlDirectives: {},
         body: Buffer.concat(anotherBody.map(x => Buffer.from(x)))
       })
@@ -133,6 +135,7 @@ function cacheStoreTests (CacheStore) {
       deepStrictEqual(await readResponse(readResult), {
         ...requestValue,
         etag: undefined,
+        vary: undefined,
         cacheControlDirectives: {},
         body: Buffer.concat(requestBody.map(x => Buffer.from(x)))
       })
@@ -208,6 +211,7 @@ function cacheStoreTests (CacheStore) {
       deepStrictEqual(await readResponse(readStream), {
         ...responseValue,
         etag: undefined,
+        vary: { 'some-header': 'hello world' },
         cacheControlDirectives: {},
         body: Buffer.concat(requestBody.map(x => Buffer.from(x)))
       })
diff --git a/test/cache-interceptor/cache-tests-worker.mjs b/test/cache-interceptor/cache-tests-worker.mjs
new file mode 100644
--- /dev/null
+++ b/test/cache-interceptor/cache-tests-worker.mjs
@@ -0,0 +1,6 @@
+'use strict'
+
+import { parentPort } from 'node:worker_threads'
+
+await import('../fixtures/cache-tests/test-engine/server/server.mjs')
+parentPort.postMessage('listening')
diff --git a/test/cache-interceptor/cache-tests.mjs b/test/cache-interceptor/cache-tests.mjs
new file mode 100644
--- /dev/null
+++ b/test/cache-interceptor/cache-tests.mjs
@@ -0,0 +1,339 @@
+'use strict'
+
+import { Worker } from 'node:worker_threads'
+import { parseArgs, styleText } from 'node:util'
+import { join } from 'node:path'
+import { tmpdir } from 'node:os'
+import { once } from 'node:events'
+import { Agent, interceptors, setGlobalDispatcher, fetch } from '../../index.js'
+import MemoryCacheStore from '../../lib/cache/memory-cache-store.js'
+import {
+  getResults,
+  runTests as runTestSuite
+} from '../fixtures/cache-tests/test-engine/client/runner.mjs'
+import tests from '../fixtures/cache-tests/tests/index.mjs'
+import { testResults, testUUIDs } from '../fixtures/cache-tests/test-engine/client/test.mjs'
+import { determineTestResult, testLookup } from '../fixtures/cache-tests/test-engine/lib/results.mjs'
+
+/**
+ * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheOptions} CacheOptions
+ *
+ * @typedef {{
+ *  opts: CacheOptions,
+ *  ignoredTests?: string[],
+ * }} TestEnvironment
+ *
+ * @typedef {{
+ *  total: number,
+ *  skipped: number,
+ *  passed: number,
+ *  failed: number,
+ *  optionalFailed: number,
+ *  setupFailed: number,
+ *  testHarnessFailed: number,
+ *  dependencyFailed: number,
+ *  retried: number
+ * }} TestStats
+ */
+
+const CLI_OPTIONS = parseArgs({
+  options: {
+    type: {
+      type: 'string',
+      multiple: true,
+      short: 't'
+    }
+  }
+})
+
+/**
+ * @type {TestEnvironment[]}
+ */
+const CACHE_TYPES = [
+  {
+    opts: { type: 'shared', methods: ['GET', 'HEAD'] },
+    ignoredTests: [
+      'freshness-max-age-s-maxage-private',
+      'freshness-max-age-s-maxage-private-multiple'
+    ]
+  },
+  {
+    opts: { type: 'private', methods: ['GET', 'HEAD'] }
+  }
+]
+
+/**
+ * @type {TestEnvironment[]}
+ */
+const CACHE_STORES = [
+  { opts: { store: new MemoryCacheStore() } }
+]
+
+const PROTOCOL = 'http'
+const PORT = 8000
+const BASE_URL = `${PROTOCOL}://localhost:${PORT}`
+const PIDFILE = join(tmpdir(), 'http-cache-test-server.pid')
+
+console.log(`PROTOCOL: ${styleText('gray', PROTOCOL)}`)
+console.log(`    PORT: ${styleText('gray', `${PORT}`)}`)
+console.log(`BASE_URL: ${styleText('gray', BASE_URL)}`)
+console.log(` PIDFILE: ${styleText('gray', PIDFILE)}`)
+console.log('')
+
+const testEnvironments = filterEnvironments(
+  buildTestEnvironments(0, [CACHE_TYPES, CACHE_STORES])
+)
+
+console.log(`Testing ${testEnvironments.length} environments`)
+
+for (const environment of testEnvironments) {
+  console.log('TEST ENVIRONMENT')
+  if (environment.opts.store) {
+    console.log(`          store: ${styleText('gray', environment.opts.store?.constructor.name ?? 'undefined')}`)
+  }
+  if (environment.opts.methods) {
+    console.log(`        methods: ${styleText('gray', JSON.stringify(environment.opts.methods) ?? 'undefined')}`)
+  }
+  if (environment.opts.cacheByDefault) {
+    console.log(` cacheByDefault: ${styleText('gray', `${environment.opts.cacheByDefault}`)}`)
+  }
+  if (environment.opts.type) {
+    console.log(`           type: ${styleText('gray', environment.opts.type)}`)
+  }
+  if (environment.ignoredTests) {
+    console.log(`  ignored tests: ${styleText('gray', JSON.stringify(environment.ignoredTests))}`)
+  }
+
+  try {
+    await runTests(environment)
+  } catch (err) {
+    console.error(err)
+  }
+
+  const stats = printResults(environment, getResults())
+  printStats(stats)
+
+  // Cleanup state
+  for (const key of Object.keys(testUUIDs)) {
+    delete testUUIDs[key]
+  }
+  for (const key of Object.keys(testResults)) {
+    delete testResults[key]
+  }
+
+  console.log('')
+}
+
+/**
+ * @param {number} idx
+ * @param  {TestEnvironment[][]} testOptions
+ * @returns {TestEnvironment[]}
+ */
+function buildTestEnvironments (idx, testOptions) {
+  const baseEnvironments = testOptions[idx]
+
+  if (idx + 1 >= testOptions.length) {
+    // We're at the end, nothing more to make a matrix out of
+    return baseEnvironments
+  }
+
+  /**
+   * @type {TestEnvironment[]}
+   */
+  const environments = []
+
+  // Get all of the environments below us
+  const subEnvironments = buildTestEnvironments(idx + 1, testOptions)
+
+  for (const baseEnvironment of baseEnvironments) {
+    const combinedEnvironments = subEnvironments.map(subEnvironment => {
+      const ignoredTests = baseEnvironment.ignoredTests ?? []
+      if (subEnvironment.ignoredTests) {
+        ignoredTests.push(...subEnvironment.ignoredTests)
+      }
+
+      return {
+        opts: {
+          ...baseEnvironment.opts,
+          ...subEnvironment.opts
+        },
+        ignoredTests: ignoredTests.length > 0 ? ignoredTests : undefined
+      }
+    })
+
+    environments.push(...combinedEnvironments)
+  }
+
+  return environments
+}
+
+/**
+ * @param {TestEnvironment[]} environments
+ * @returns {TestEnvironment[]}
+ */
+function filterEnvironments (environments) {
+  const { values } = CLI_OPTIONS
+
+  if (values.type) {
+    environments = environments.filter(env =>
+      env.opts.type === undefined ||
+      values.type?.includes(env.opts.type)
+    )
+  }
+
+  return environments
+}
+
+/**
+ * @param {TestEnvironment} environment
+ */
+async function runTests (environment) {
+  // Start the test server. We use a worker here since the suite doesn't expose it
+  const worker = new Worker(join(import.meta.dirname, 'cache-tests-worker.mjs'), {
+    env: {
+      npm_config_protocol: PROTOCOL,
+      npm_config_port: `${PORT}`,
+      npm_config_pidfile: PIDFILE
+    }
+  })
+
+  try {
+    await once(worker, 'message', { signal: AbortSignal.timeout(5000) })
+
+    const client = new Agent().compose(interceptors.cache(environment.opts))
+    setGlobalDispatcher(client)
+
+    // Run the tests
+    await runTestSuite(tests, fetch, true, BASE_URL)
+  } finally {
+    await worker.terminate()
+  }
+}
+
+/**
+ * @param {TestEnvironment} environment
+ * @param {any} results
+ * @returns {TestStats}
+ */
+function printResults (environment, results) {
+  /**
+   * @type {TestStats}
+   */
+  const stats = {
+    // TODO this won't always be this
+    total: Object.keys(results).length - (environment.ignoredTests?.length || 0),
+    skipped: 0,
+    passed: 0,
+    failed: 0,
+    optionalFailed: 0,
+    setupFailed: 0,
+    testHarnessFailed: 0,
+    dependencyFailed: 0,
+    retried: 0
+  }
+
+  for (const testId in results) {
+    if (environment.ignoredTests?.includes(testId)) {
+      continue
+    }
+
+    const test = testLookup(tests, testId)
+    // eslint-disable-next-line no-unused-vars
+    const [code, _, icon] = determineTestResult(tests, testId, results, false)
+
+    let status
+    let color
+    switch (code) {
+      case '-':
+        status = 'skipped'
+        color = 'gray'
+        stats.skipped++
+        break
+      case '\uf058':
+        status = 'pass'
+        color = 'green'
+        stats.passed++
+        break
+      case '\uf057':
+        status = 'failed'
+        color = 'red'
+        stats.failed++
+        break
+      case '\uf05a':
+        status = 'failed (optional)'
+        color = 'yellow'
+        stats.optionalFailed++
+        break
+      case '\uf055':
+        status = 'yes'
+        color = 'green'
+        stats.passed++
+        break
+      case '\uf056':
+        status = 'no'
+        color = 'red'
+        stats.failed++
+        break
+      case '\uf059':
+        status = 'setup failure'
+        color = 'red'
+        stats.setupFailed++
+        break
+      case '\uf06a':
+        status = 'test harness failure'
+        color = 'red'
+        stats.testHarnessFailed++
+        break
+      case '\uf192':
+        status = 'dependency failure'
+        color = 'red'
+        stats.dependencyFailed++
+        break
+      case '\uf01e':
+        status = 'retry'
+        color = 'yellow'
+        stats.retried++
+        break
+      default:
+        status = 'unknown'
+        color = ['strikethrough', 'white']
+        break
+    }
+
+    console.log(`${icon} ${styleText(color, `${status} - ${test.name}`)} (${styleText('gray', testId)})`)
+
+    if (results[testId] !== true) {
+      const [type, message] = results[testId]
+      console.log(`    ${styleText(color, `${type}: ${message}`)}`)
+    }
+  }
+
+  return stats
+}
+
+/**
+ * @param {TestStats} stats
+ */
+function printStats (stats) {
+  const {
+    total,
+    skipped,
+    passed,
+    failed,
+    optionalFailed,
+    setupFailed,
+    testHarnessFailed,
+    dependencyFailed,
+    retried
+  } = stats
+
+  console.log(`\n        Total tests: ${total}`)
+  console.log(`            ${styleText('gray', 'Skipped')}: ${skipped} (${((skipped / total) * 100).toFixed(1)}%)`)
+  console.log(`             ${styleText('green', 'Passed')}: ${passed} (${((passed / total) * 100).toFixed(1)}%)`)
+  console.log(`             ${styleText('red', 'Failed')}: ${failed} (${((failed / total) * 100).toFixed(1)}%)`)
+  console.log(`  ${styleText('yellow', 'Failed (optional)')}: ${optionalFailed} (${((optionalFailed / total) * 100).toFixed(1)}%)`)
+  console.log(`       ${styleText('red', 'Setup failed')}: ${setupFailed} (${((setupFailed / total) * 100).toFixed(1)}%)`)
+  console.log(`${styleText('red', 'Test Harness Failed')}: ${testHarnessFailed} (${((testHarnessFailed / total) * 100).toFixed(1)}%)`)
+  console.log(`  ${styleText('red', 'Dependency Failed')}: ${dependencyFailed} (${((dependencyFailed / total) * 100).toFixed(1)}%)`)
+  console.log(`            ${styleText('yellow', 'Retried')}: ${retried} (${((retried / total) * 100).toFixed(1)}%)`)
+}
diff --git a/test/cache-interceptor/sqlite-cache-store-tests.js b/test/cache-interceptor/sqlite-cache-store-tests.js
--- a/test/cache-interceptor/sqlite-cache-store-tests.js
+++ b/test/cache-interceptor/sqlite-cache-store-tests.js
@@ -69,6 +69,7 @@ test('SqliteCacheStore works nicely with multiple stores', async (t) => {
   deepStrictEqual(await readResponse(readable), {
     ...requestValue,
     etag: undefined,
+    vary: undefined,
     cacheControlDirectives: undefined,
     body: Buffer.concat(requestBody.map(x => Buffer.from(x)))
   })
@@ -79,6 +80,7 @@ test('SqliteCacheStore works nicely with multiple stores', async (t) => {
   deepStrictEqual(await readResponse(readable), {
     ...requestValue,
     etag: undefined,
+    vary: undefined,
     cacheControlDirectives: undefined,
     body: Buffer.concat(requestBody.map(x => Buffer.from(x)))
   })
diff --git a/test/fixtures/cache-tests/.editorconfig b/test/fixtures/cache-tests/.editorconfig
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/.editorconfig
@@ -0,0 +1,16 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+charset = utf-8
+trim_trailing_whitespace = true
+max_line_length = 100
+indent_size = 2
+
+[*.py]
+indent_size = 4
+
+[Makefile]
+indent_style = tab
diff --git a/test/fixtures/cache-tests/.github/dependabot.yml b/test/fixtures/cache-tests/.github/dependabot.yml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/.github/dependabot.yml
@@ -0,0 +1,15 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+  - package-ecosystem: "github-actions" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"
+  - package-ecosystem: "npm" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"
diff --git a/test/fixtures/cache-tests/.github/workflows/lint.yml b/test/fixtures/cache-tests/.github/workflows/lint.yml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/.github/workflows/lint.yml
@@ -0,0 +1,18 @@
+name: Lint
+
+on: [push, pull_request]
+
+jobs:
+  validate:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v4
+    - uses: actions/setup-node@v4
+      with:
+        node-version: '14'
+    - run: npm install -g standard
+    - run: npm install ajv
+    - run: npm run lint
+    - run: npm run validate
diff --git a/test/fixtures/cache-tests/.github/workflows/test.yml b/test/fixtures/cache-tests/.github/workflows/test.yml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/.github/workflows/test.yml
@@ -0,0 +1,20 @@
+name: Cache Tests
+on: workflow_dispatch
+
+jobs:
+  test:
+
+    runs-on: macOS-latest
+
+    steps:
+    - uses: actions/checkout@v4
+    - uses: actions/setup-node@v4
+      with:
+        node-version: '14'
+    - name: Run browser tests
+      run: ./test-browser.sh
+    - name: Push update
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      run: |
+        ./update.sh
diff --git a/test/fixtures/cache-tests/.gitignore b/test/fixtures/cache-tests/.gitignore
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+package-lock.json
+server.pid
diff --git a/test/fixtures/cache-tests/CNAME b/test/fixtures/cache-tests/CNAME
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/CNAME
@@ -0,0 +1 @@
+cache-tests.fyi
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/CONTRIBUTING.md b/test/fixtures/cache-tests/CONTRIBUTING.md
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/CONTRIBUTING.md
@@ -0,0 +1,108 @@
+
+# Contributing
+
+Contributions from cache vendors and users are welcome.
+
+Over time we'll document guidelines and best practices for contribution, but in the meantime, feel free to file issues and create PRs.
+
+
+## Test Format
+
+Each test run gets its own URL, randomized content, and operates independently.
+
+Tests are kept in JavaScript files in `tests/`, each file representing a suite.
+
+A suite is an object with the following members:
+
+- `name` - A concise description of the suite. Required.
+- `id` - A short, stable identifier for the suite. Required.
+- `description` - A longer description of the suite, can contain Markdown. Optional.
+- `spec_anchors` - An array of strings that represent anchors in the HTTP Caching specification related to this suite. Optional.
+- `tests` - see below.
+
+E.g.,
+
+```javascript
+export default {
+  name: 'Example Tests',
+  id: 'example',
+  description: 'These are the `Foo` tests!'
+  tests: [ ... ]
+}
+```
+
+The `tests` member is an array of objects, with the following members:
+
+- `name` - A concise description of the test. Can contain Markdown. Required.
+- `id` - A short, stable identifier for the test. Required.
+- `description` - Longer details of the test. Optional.
+- `kind` - One of:
+  - `required` - This is a conformance test for a requirement in the standard. Default.
+  - `optimal` - This test is to see if the cache behaves optimally.
+  - `check` - This test is gathering information about cache behaviour.
+- `requests` - a list of request objects (see below).
+- `browser_only` - if `true`, will only run on browser caches. Default `false`.
+- `cdn_only` - if `true`, will only run on CDN caches. Default `false`.
+- `browser_skip` - if `true, will not run on browser caches. Default `false`.
+- `depends_on` - a list of test IDs that, when one fails, indicates that this test's results are not useful. Currently limited to test IDs in the same suite. Optional.
+- `spec_anchors` - An array of strings that represent anchors in the HTTP Caching specification related to this test. Optional.
+
+Possible members of a request object:
+
+- `request_method` - A string containing the HTTP method to be used. Default `GET`.
+- `request_headers` - An array of `[header_name_string, header_value_string]` arrays to
+                    emit in the request.
+- `request_body` - A string to use as the request body.
+- `query_arg` - query arguments to add.
+- `filename` - filename to use.
+- `mode` - The mode string to pass to `fetch()`.
+- `credentials` - The credentials string to pass to `fetch()`.
+- `cache` - The cache string to pass to `fetch()`.
+- `redirect` - The redirect string to pass to `fetch()`.
+- `pause_after` - Boolean controlling a 3-second pause after the request completes.
+- `disconnect` - Close the connection when receiving this request.
+- `magic_locations` - Boolean; if `true`, the `Location` and `Content-Location` response headers will be rewritten to full URLs.
+- `magic_ims` - Boolean; if `true`, the `If-Modified-Since` request header will be written as a delta against the previous response's `Last-Modified`, instead of `now`.
+- `rfc850date` - Array of header names to use RFC850 format on when magically converting dates.
+- `response_status` - A `[number, string]` array containing the HTTP status code
+                    and phrase to return from the origin. Default `200` or `304`.
+- `response_headers` - An array of `[header_name_string, header_value_string]` arrays to
+                     emit in the origin response. These values will also be checked like
+                     expected_response_headers, unless there is a third value that is
+                     `false`.
+- `response_body` - String to send as the response body from the origin. Defaults to
+                  the test identifier.
+- `response_pause` - Integer number of seconds for the server to pause before generating a response.
+- `check_body` - Whether to check the response body. Default `true`.
+- `expected_type` - One of:
+  - `cached`: The response is served from cache
+  - `not_cached`: The response is not served from cache; it comes from the origin
+  - `lm_validated`: The response comes from cache, but was validated on the origin with Last-Modified
+  - `etag_validated`: The response comes from cache, but was validated on the origin with an ETag
+- `expected_method` - A string HTTP method; checked on the server.
+- `expected_status` - A numeric HTTP status code; checked on the client.
+                    If not set, the value of `response_status[0]` will be used; if that
+                    is not set, 200 will be used.
+- `expected_request_headers` - An array of `[header_name_string, header_value_string]` representing
+                              headers to check the request for on the server, or an array of
+                              strings representing header names to check for presence in the
+                              request.
+- `expected_request_headers_missing` - An array of `[header_name_string, header_value_string]`
+                                       representing headers to check for absence in the request for on the server, or an array of strings representing header names to check for absence in the request.
+- `expected_response_headers` - An array of any combination of the following. See also `response_headers`.
+  - `header_name_string`: assert that the named header is present
+  - `[header_name_string, header_value_string]`: assert that the header has the given value
+  - `[header_name_string, '=', other_header_name]`: assert that the two headers have the same value
+  - `[header_name_string, '>', number]`: assert that the header's value is numerically greater than specified
+- `expected_response_headers_missing` - An array of any combination of the following.
+  - `header_name_string` representing headers to check that the response on the client does not include.
+  - `[header_name_string, header_value_string]`: headers to check that the response is either missing, or if they're present, that they do _not_ contain the given value string (evaluated against the whole header value).
+- `expected_response_text` - A string to check the response body against on the client.
+- `setup` - Boolean to indicate whether this is a setup request; failures don't mean the actual test failed.
+- `setup_tests` - Array of values that indicate whether the specified check is part of setup; failures don't mean the actual test failed. One of: `["expected_type", "expected_method", "expected_status", "expected_response_headers", "expected_response_text", "expected_request_headers"]`
+
+`server.js` stashes an entry containing observed headers for each request it receives. When the
+test fetches have run, this state is retrieved and the expected_* lists are checked, including
+their length.
+
+For convenience and clarity when writing tests, there are some request templates available in `templates.mjs`. Each template is a function which accepts a request object, as defined above, and returns a new request object. Any fields in the template are added to the request object unless a field of the same name is already present.
diff --git a/test/fixtures/cache-tests/LICENSE b/test/fixtures/cache-tests/LICENSE
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2018, Mark Nottingham
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/test/fixtures/cache-tests/README.md b/test/fixtures/cache-tests/README.md
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/README.md
@@ -0,0 +1,187 @@
+# Tests for HTTP Caches
+
+This is a test suite for the behaviours of [HTTP caches](https://httpwg.org/specs/rfc9111.html),
+including browsers, proxy caches and CDNs. Its public results are available at
+[cache-tests.fyi](https://cache-tests.fyi).
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+
+- [Goals](#goals)
+- [Installation](#installation)
+  - [Installing from NPM](#installing-from-npm)
+- [Running tests with scripts](#running-tests-with-scripts)
+  - [Testing with docker](#testing-with-docker)
+  - [Testing browser caches](#testing-browser-caches)
+  - [Testing forward proxies](#testing-forward-proxies)
+- [Running tests with npm](#running-tests-with-npm)
+  - [Starting the test server](#starting-the-test-server)
+  - [Running the client](#running-the-client)
+- [Interpreting results](#interpreting-results)
+  - [Test results FAQ](#test-results-faq)
+- [Getting your results onto cache-tests.fyi](#getting-your-results-onto-cache-testsfyi)
+- [Creating new tests](#creating-new-tests)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+
+## Goals
+
+Overall, the goal of these tests is to identify variances in the behaviour, both from the normative specifications and between implementations. This in turn can help avoid situations where they act in surprising ways.
+
+The underlying aim is to provide a basis for discussion about how HTTP caches -- especially in CDNs and reverse proxies -- should behave, so that over time we can adapt the tests and align implementations to behave more consistently.
+
+In other words, **passing all of the tests currently means nothing** -- this is not a conformance test suite, it's just the start of a conversation, and a **tool to assess how a cache behaves**.
+
+Therefore, if you believe a test should change (based upon common behaviour or your interpretation of the specifications), or have additional tests, please [contribute](CONTRIBUTING.md).
+
+
+## Installation
+
+The tests require a recent version of [NodeJS](https://nodejs.org/) (14.8.0 or greater), which includes the `npm` package manager.
+
+To install the most recent source from GitHub (*recommended; things are moving fast*):
+
+> git clone https://github.com/http-tests/cache-tests.git
+
+and then install dependencies:
+
+> cd cache-tests; npm i
+
+### Installing from NPM
+
+Alternatively, for the most recent release:
+
+> npm i --legacy-bundling http-cache-tests
+
+Note that the version in the registry is not necessarily up-to-date.
+
+
+## Running tests with scripts
+
+A number of scripts are supplied to simplify common test scenarios.
+
+### Testing with docker
+
+The `mnot/proxy-cache-tests` Docker image can be used to test common reverse proxy caches. Once you have docker running, you can run the CLI tests against a given proxy like this:
+
+> ./test-docker.sh squid
+
+Omit the proxy name to test all available in the Docker image. To run an individual test case, try:
+
+> ./test-docker.sh -i freshness-none nginx
+
+
+### Testing browser caches
+
+To test a browser, just point it at `https://{hostname:port}/test-browser.html` after setting up the server.
+
+On OSX, you can use `test-browser.sh` to automate this:
+
+> ./test-browser.sh safari
+
+Again, omit the browser name to test all. Run a single case with:
+
+> ./test-browser.sh -i freshness-none safari
+
+Make sure that your browsers are not configured to use a proxy cache, and that the network being tested upon does not use an intercepting proxy cache.
+
+
+### Testing forward proxies
+
+To test a forward proxy which listens on 127.0.0.1:8082, start the server:
+
+> npm run server
+
+and then run:
+
+> HTTP_PROXY=http://127.0.0.1:8082 npm run --silent cli --base=http://127.0.0.1:8000
+
+or:
+
+> ./test-host.sh 127.0.0.1:8002
+
+Again, pass `-i` to run a specific test.
+
+
+## Running tests with npm
+
+If you don't want to run the test scripts (see above), this section documents how to run tests directly with NPM. First, you'll need to start the test server; then, you'll need to run the client against it.
+
+### Starting the test server
+
+First, start the server-side by running:
+
+> npm run server
+
+inside the directory (the repository's directory if you cloned from git, or `node_modules/http-cache-tests` if you installed from npm).
+
+By default, the server runs on port 8000; to choose a different port, use the `--port` argument; e.g.,
+
+> npm run server --port=8080
+
+If you want to run an HTTPS origin, you'll need to specify the `protocol`, `keyfile` and `certfile`:
+
+> npm run server --protocol=https --keyfile=/path/to/key.pem --certfile=/path-to-cert.pem
+
+Note that the default port for HTTPS is still 8000.
+
+
+### Running the client
+
+To test a reverse proxy or CDN from the command line:
+
+> npm run --silent cli --base=http://server-url.example.org:8000/
+
+... using the URL of the server you want to test. This will output the test results in JSON to STDOUT, suitable for inclusion in the `results` directory. See `lib/summary.mjs` for details of how to interpret that.
+
+To run a single test, use:
+
+> npm run cli --base=http://server-url.example.org:8000/ --id=test-id
+
+... where `test-id` is the identifier for the test. This will output the request and response headers as seen by the client and server, along with the results. This is useful for debugging a particular failure.
+
+
+## Interpreting results
+
+HTTP caching by its nature is an optimisation; implementations aren't required to cache everything. However, when they do cache, their behaviour is constrained by [the specification](https://httpwg.org/specs/rfc9111.html).
+
+As a result, there are a few different kinds of test results (note that the HTML results use similar but slightly different symbols):
+
+* ✅ - The test was successful.
+* ⛔️ - The test failed, and likely indicates a specification conformance problem.
+* ⚠️ - The cache didn't behave in an optimal fashion (usually, it didn't use a stored response when it could have), but this is not a conformance problem.
+* ● / ○ - These are tests to see how deployed caches behave; we use them to gather information for future specification work. "yes" and "no" respectively.
+
+Some additional results might pop up from time to time:
+
+* ⁉️ - The test harness failed; this is an internal error, please [file a bug if one doesn't exist](https://github.com/http-tests/cache-tests/issues/).
+* 🔹 - The test failed during setup; something interfered with the harness's communication between the client and server. See below.
+* ↻ - The cache retried a request; this means the test result needs to be interpreted manually, as it may or may not have behaved correctly.
+* ⚪️ - Another test that this test depends on has failed; we use dependencies to help assure that we're actually testing the behaviour in question.
+* `-` - Not tested; usually because the test isn't applicable to this cache.
+
+When you're testing with a browser, each test has a `uuid` that identifies that specific test run; this can be used to find its requests in the browser developer tools or proxy logs. Click ⚙︎ to copy it to the clipboard.
+
+
+### Test results FAQ
+
+If you see a lot of failures, it might be one of a few different issues:
+
+* If you see lots of grey circles at the top (dependency failures), it's probably because the cache will store and reuse a response without explicit freshness or a validator; see the very first test (`freshness-none`). While this is technically legal in HTTP, it interferes with the tests. Disabling "default caching" or similar usually fixes this.
+
+* If you see lots of blue diamonds (setup failures), it's likely that the cache is refusing `PUT` requests. Enable them to clear this; the tests use PUT to synchronise state between the client and the server.
+
+
+## Getting your results onto cache-tests.fyi
+
+[cache-tests.fyi](https://cache-tests.fyi) collects results from caches in browsers, reverse proxies, and CDNs. Its purpose is to gather information about how HTTP caching works "in the wild", to help the [HTTP Working Group](https://httpwg.org) make decisions about how to evolve the specification.
+
+If your implementation isn't listed and you want it to be, please file an issue, or contact [Mark Nottingham](mailto:mnot@mnot.net). Both open source and proprietary implementations are welcome; if there are commercial concerns about disclosing your results, your identity can be anonymised (e.g., "CDN A"), and will not be disclosed to anyone.
+
+Right now, all of the reverse proxy and CDN implementations are run by a script on a server, using the command-line client; to keep results up-to-date as the tests evolve, it's most helpful if you can provide an endpoint to test (for reverse proxies and CDNs).
+
+
+## Creating new tests
+
+See [CONTRIBUTING.md](CONTRIBUTING.md)
diff --git a/test/fixtures/cache-tests/asset/badge.png b/test/fixtures/cache-tests/asset/badge.png
new file mode 100644
Binary files /dev/null and b/test/fixtures/cache-tests/asset/badge.png differ
diff --git a/test/fixtures/cache-tests/asset/fonts/FontAwesome.otf b/test/fixtures/cache-tests/asset/fonts/FontAwesome.otf
new file mode 100644
Binary files /dev/null and b/test/fixtures/cache-tests/asset/fonts/FontAwesome.otf differ
diff --git a/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.eot b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.eot
new file mode 100644
Binary files /dev/null and b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.eot differ
diff --git a/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.svg b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.svg
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg>
+<metadata>
+Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016
+ By ,,,
+Copyright Dave Gandy 2016. All rights reserved.
+</metadata>
+<defs>
+<font id="FontAwesome" horiz-adv-x="1536" >
+  <font-face 
+    font-family="FontAwesome"
+    font-weight="400"
+    font-stretch="normal"
+    units-per-em="1792"
+    panose-1="0 0 0 0 0 0 0 0 0 0"
+    ascent="1536"
+    descent="-256"
+    bbox="-1.02083 -256.962 2304.6 1537.02"
+    underline-thickness="0"
+    underline-position="0"
+    unicode-range="U+0020-F500"
+  />
+<missing-glyph horiz-adv-x="896" 
+d="M224 112h448v1312h-448v-1312zM112 0v1536h672v-1536h-672z" />
+    <glyph glyph-name=".notdef" horiz-adv-x="896" 
+d="M224 112h448v1312h-448v-1312zM112 0v1536h672v-1536h-672z" />
+    <glyph glyph-name=".null" horiz-adv-x="0" 
+ />
+    <glyph glyph-name="nonmarkingreturn" horiz-adv-x="597" 
+ />
+    <glyph glyph-name="space" unicode=" " horiz-adv-x="448" 
+ />
+    <glyph glyph-name="dieresis" unicode="&#xa8;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="copyright" unicode="&#xa9;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="registered" unicode="&#xae;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="acute" unicode="&#xb4;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="AE" unicode="&#xc6;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="Oslash" unicode="&#xd8;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="trademark" unicode="&#x2122;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="infinity" unicode="&#x221e;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="notequal" unicode="&#x2260;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="glass" unicode="&#xf000;" horiz-adv-x="1792" 
+d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
+    <glyph glyph-name="music" unicode="&#xf001;" 
+d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89
+t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="search" unicode="&#xf002;" horiz-adv-x="1664" 
+d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5
+t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
+    <glyph glyph-name="envelope" unicode="&#xf003;" horiz-adv-x="1792" 
+d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13
+t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z
+M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="heart" unicode="&#xf004;" horiz-adv-x="1792" 
+d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600
+q-18 -18 -44 -18z" />
+    <glyph glyph-name="star" unicode="&#xf005;" horiz-adv-x="1664" 
+d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455
+l502 -73q56 -9 56 -46z" />
+    <glyph glyph-name="star_empty" unicode="&#xf006;" horiz-adv-x="1664" 
+d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500
+l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
+    <glyph glyph-name="user" unicode="&#xf007;" horiz-adv-x="1280" 
+d="M1280 137q0 -109 -62.5 -187t-150.5 -78h-854q-88 0 -150.5 78t-62.5 187q0 85 8.5 160.5t31.5 152t58.5 131t94 89t134.5 34.5q131 -128 313 -128t313 128q76 0 134.5 -34.5t94 -89t58.5 -131t31.5 -152t8.5 -160.5zM1024 1024q0 -159 -112.5 -271.5t-271.5 -112.5
+t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+    <glyph glyph-name="film" unicode="&#xf008;" horiz-adv-x="1920" 
+d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128
+q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45
+t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128
+q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19
+t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="th_large" unicode="&#xf009;" horiz-adv-x="1664" 
+d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38
+h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="th" unicode="&#xf00a;" horiz-adv-x="1792" 
+d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320
+q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28
+h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192
+q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="th_list" unicode="&#xf00b;" horiz-adv-x="1792" 
+d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960
+q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28
+h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="ok" unicode="&#xf00c;" horiz-adv-x="1792" 
+d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
+    <glyph glyph-name="remove" unicode="&#xf00d;" horiz-adv-x="1408" 
+d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68
+t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
+    <glyph glyph-name="zoom_in" unicode="&#xf00e;" horiz-adv-x="1664" 
+d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224
+q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5
+t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
+    <glyph glyph-name="zoom_out" unicode="&#xf010;" horiz-adv-x="1664" 
+d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z
+M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z
+" />
+    <glyph glyph-name="off" unicode="&#xf011;" 
+d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5
+t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
+    <glyph glyph-name="signal" unicode="&#xf012;" horiz-adv-x="1792" 
+d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23
+v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="cog" unicode="&#xf013;" 
+d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38
+q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13
+l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22
+q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
+    <glyph glyph-name="trash" unicode="&#xf014;" horiz-adv-x="1408" 
+d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576
+q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832
+q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="home" unicode="&#xf015;" horiz-adv-x="1664" 
+d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5
+l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
+    <glyph glyph-name="file_alt" unicode="&#xf016;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+" />
+    <glyph glyph-name="time" unicode="&#xf017;" 
+d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="road" unicode="&#xf018;" horiz-adv-x="1920" 
+d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256
+q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
+    <glyph glyph-name="download_alt" unicode="&#xf019;" horiz-adv-x="1664" 
+d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136
+q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
+    <glyph glyph-name="download" unicode="&#xf01a;" 
+d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273
+t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="upload" unicode="&#xf01b;" 
+d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198
+t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="inbox" unicode="&#xf01c;" 
+d="M1023 576h316q-1 3 -2.5 8.5t-2.5 7.5l-212 496h-708l-212 -496q-1 -3 -2.5 -8.5t-2.5 -7.5h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552
+q25 -61 25 -123z" />
+    <glyph glyph-name="play_circle" unicode="&#xf01d;" 
+d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="repeat" unicode="&#xf01e;" 
+d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q15 0 25 -9
+l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
+    <glyph glyph-name="refresh" unicode="&#xf021;" 
+d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117
+q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5
+q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
+    <glyph glyph-name="list_alt" unicode="&#xf022;" horiz-adv-x="1792" 
+d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z
+M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5
+t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47
+t47 -113z" />
+    <glyph glyph-name="lock" unicode="&#xf023;" horiz-adv-x="1152" 
+d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="flag" unicode="&#xf024;" horiz-adv-x="1792" 
+d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48
+t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="headphones" unicode="&#xf025;" horiz-adv-x="1664" 
+d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78
+t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5
+t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
+    <glyph glyph-name="volume_off" unicode="&#xf026;" horiz-adv-x="768" 
+d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
+    <glyph glyph-name="volume_down" unicode="&#xf027;" horiz-adv-x="1152" 
+d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 36
+t12 56.5t-12 56.5t-29 36t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
+    <glyph glyph-name="volume_up" unicode="&#xf028;" horiz-adv-x="1664" 
+d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 36
+t12 56.5t-12 56.5t-29 36t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5
+t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289
+t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
+    <glyph glyph-name="qrcode" unicode="&#xf029;" horiz-adv-x="1408" 
+d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z
+M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
+    <glyph glyph-name="barcode" unicode="&#xf02a;" horiz-adv-x="1792" 
+d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z
+M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
+    <glyph glyph-name="tag" unicode="&#xf02b;" 
+d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5
+l715 -714q37 -39 37 -91z" />
+    <glyph glyph-name="tags" unicode="&#xf02c;" horiz-adv-x="1920" 
+d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5
+l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
+    <glyph glyph-name="book" unicode="&#xf02d;" horiz-adv-x="1664" 
+d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23
+q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906
+q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5
+t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
+    <glyph glyph-name="bookmark" unicode="&#xf02e;" horiz-adv-x="1280" 
+d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
+    <glyph glyph-name="print" unicode="&#xf02f;" horiz-adv-x="1664" 
+d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68
+v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
+    <glyph glyph-name="camera" unicode="&#xf030;" horiz-adv-x="1920" 
+d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136
+q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="font" unicode="&#xf031;" horiz-adv-x="1664" 
+d="M725 977l-170 -450q33 0 136.5 -2t160.5 -2q19 0 57 2q-87 253 -184 452zM0 -128l2 79q23 7 56 12.5t57 10.5t49.5 14.5t44.5 29t31 50.5l237 616l280 724h75h53q8 -14 11 -21l205 -480q33 -78 106 -257.5t114 -274.5q15 -34 58 -144.5t72 -168.5q20 -45 35 -57
+q19 -15 88 -29.5t84 -20.5q6 -38 6 -57q0 -5 -0.5 -13.5t-0.5 -12.5q-63 0 -190 8t-191 8q-76 0 -215 -7t-178 -8q0 43 4 78l131 28q1 0 12.5 2.5t15.5 3.5t14.5 4.5t15 6.5t11 8t9 11t2.5 14q0 16 -31 96.5t-72 177.5t-42 100l-450 2q-26 -58 -76.5 -195.5t-50.5 -162.5
+q0 -22 14 -37.5t43.5 -24.5t48.5 -13.5t57 -8.5t41 -4q1 -19 1 -58q0 -9 -2 -27q-58 0 -174.5 10t-174.5 10q-8 0 -26.5 -4t-21.5 -4q-80 -14 -188 -14z" />
+    <glyph glyph-name="bold" unicode="&#xf032;" horiz-adv-x="1408" 
+d="M555 15q74 -32 140 -32q376 0 376 335q0 114 -41 180q-27 44 -61.5 74t-67.5 46.5t-80.5 25t-84 10.5t-94.5 2q-73 0 -101 -10q0 -53 -0.5 -159t-0.5 -158q0 -8 -1 -67.5t-0.5 -96.5t4.5 -83.5t12 -66.5zM541 761q42 -7 109 -7q82 0 143 13t110 44.5t74.5 89.5t25.5 142
+q0 70 -29 122.5t-79 82t-108 43.5t-124 14q-50 0 -130 -13q0 -50 4 -151t4 -152q0 -27 -0.5 -80t-0.5 -79q0 -46 1 -69zM0 -128l2 94q15 4 85 16t106 27q7 12 12.5 27t8.5 33.5t5.5 32.5t3 37.5t0.5 34v35.5v30q0 982 -22 1025q-4 8 -22 14.5t-44.5 11t-49.5 7t-48.5 4.5
+t-30.5 3l-4 83q98 2 340 11.5t373 9.5q23 0 68 -0.5t68 -0.5q70 0 136.5 -13t128.5 -42t108 -71t74 -104.5t28 -137.5q0 -52 -16.5 -95.5t-39 -72t-64.5 -57.5t-73 -45t-84 -40q154 -35 256.5 -134t102.5 -248q0 -100 -35 -179.5t-93.5 -130.5t-138 -85.5t-163.5 -48.5
+t-176 -14q-44 0 -132 3t-132 3q-106 0 -307 -11t-231 -12z" />
+    <glyph glyph-name="italic" unicode="&#xf033;" horiz-adv-x="1024" 
+d="M0 -126l17 85q22 7 61.5 16.5t72 19t59.5 23.5q28 35 41 101q1 7 62 289t114 543.5t52 296.5v25q-24 13 -54.5 18.5t-69.5 8t-58 5.5l19 103q33 -2 120 -6.5t149.5 -7t120.5 -2.5q48 0 98.5 2.5t121 7t98.5 6.5q-5 -39 -19 -89q-30 -10 -101.5 -28.5t-108.5 -33.5
+q-8 -19 -14 -42.5t-9 -40t-7.5 -45.5t-6.5 -42q-27 -148 -87.5 -419.5t-77.5 -355.5q-2 -9 -13 -58t-20 -90t-16 -83.5t-6 -57.5l1 -18q17 -4 185 -31q-3 -44 -16 -99q-11 0 -32.5 -1.5t-32.5 -1.5q-29 0 -87 10t-86 10q-138 2 -206 2q-51 0 -143 -9t-121 -11z" />
+    <glyph glyph-name="text_height" unicode="&#xf034;" horiz-adv-x="1792" 
+d="M1744 128q33 0 42 -18.5t-11 -44.5l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80zM81 1407l54 -27q12 -5 211 -5q44 0 132 2
+t132 2q36 0 107.5 -0.5t107.5 -0.5h293q6 0 21 -0.5t20.5 0t16 3t17.5 9t15 17.5l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 48t-14.5 73.5t-7.5 35.5q-6 8 -12 12.5t-15.5 6t-13 2.5t-18 0.5t-16.5 -0.5
+q-17 0 -66.5 0.5t-74.5 0.5t-64 -2t-71 -6q-9 -81 -8 -136q0 -94 2 -388t2 -455q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27
+q19 42 19 383q0 101 -3 303t-3 303v117q0 2 0.5 15.5t0.5 25t-1 25.5t-3 24t-5 14q-11 12 -162 12q-33 0 -93 -12t-80 -26q-19 -13 -34 -72.5t-31.5 -111t-42.5 -53.5q-42 26 -56 44v383z" />
+    <glyph glyph-name="text_width" unicode="&#xf035;" 
+d="M81 1407l54 -27q12 -5 211 -5q44 0 132 2t132 2q70 0 246.5 1t304.5 0.5t247 -4.5q33 -1 56 31l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 47.5t-15 73.5t-7 36q-10 13 -27 19q-5 2 -66 2q-30 0 -93 1t-103 1
+t-94 -2t-96 -7q-9 -81 -8 -136l1 -152v52q0 -55 1 -154t1.5 -180t0.5 -153q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27
+q7 16 11.5 74t6 145.5t1.5 155t-0.5 153.5t-0.5 89q0 7 -2.5 21.5t-2.5 22.5q0 7 0.5 44t1 73t0 76.5t-3 67.5t-6.5 32q-11 12 -162 12q-41 0 -163 -13.5t-138 -24.5q-19 -12 -34 -71.5t-31.5 -111.5t-42.5 -54q-42 26 -56 44v383zM1310 125q12 0 42 -19.5t57.5 -41.5
+t59.5 -49t36 -30q26 -21 26 -49t-26 -49q-4 -3 -36 -30t-59.5 -49t-57.5 -41.5t-42 -19.5q-13 0 -20.5 10.5t-10 28.5t-2.5 33.5t1.5 33t1.5 19.5h-1024q0 -2 1.5 -19.5t1.5 -33t-2.5 -33.5t-10 -28.5t-20.5 -10.5q-12 0 -42 19.5t-57.5 41.5t-59.5 49t-36 30q-26 21 -26 49
+t26 49q4 3 36 30t59.5 49t57.5 41.5t42 19.5q13 0 20.5 -10.5t10 -28.5t2.5 -33.5t-1.5 -33t-1.5 -19.5h1024q0 2 -1.5 19.5t-1.5 33t2.5 33.5t10 28.5t20.5 10.5z" />
+    <glyph glyph-name="align_left" unicode="&#xf036;" horiz-adv-x="1792" 
+d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45
+t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="align_center" unicode="&#xf037;" horiz-adv-x="1792" 
+d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19
+h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="align_right" unicode="&#xf038;" horiz-adv-x="1792" 
+d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45
+t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="align_justify" unicode="&#xf039;" horiz-adv-x="1792" 
+d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45
+t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="list" unicode="&#xf03a;" horiz-adv-x="1792" 
+d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5
+t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344
+q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5
+t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192
+q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
+    <glyph glyph-name="indent_left" unicode="&#xf03b;" horiz-adv-x="1792" 
+d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5
+t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088
+q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
+    <glyph glyph-name="indent_right" unicode="&#xf03c;" horiz-adv-x="1792" 
+d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5
+t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088
+q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
+    <glyph glyph-name="facetime_video" unicode="&#xf03d;" horiz-adv-x="1792" 
+d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5
+q39 -17 39 -59z" />
+    <glyph glyph-name="picture" unicode="&#xf03e;" horiz-adv-x="1920" 
+d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216
+q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="pencil" unicode="&#xf040;" 
+d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38
+q53 0 91 -38l235 -234q37 -39 37 -91z" />
+    <glyph glyph-name="map_marker" unicode="&#xf041;" horiz-adv-x="1024" 
+d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
+    <glyph glyph-name="adjust" unicode="&#xf042;" 
+d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="tint" unicode="&#xf043;" horiz-adv-x="1024" 
+d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362
+q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
+    <glyph glyph-name="edit" unicode="&#xf044;" horiz-adv-x="1792" 
+d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832
+q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92
+l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
+    <glyph glyph-name="share" unicode="&#xf045;" horiz-adv-x="1664" 
+d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832
+q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5
+t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
+    <glyph glyph-name="check" unicode="&#xf046;" horiz-adv-x="1664" 
+d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832
+q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110
+q24 -24 24 -57t-24 -57z" />
+    <glyph glyph-name="move" unicode="&#xf047;" horiz-adv-x="1792" 
+d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45
+t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
+    <glyph glyph-name="step_backward" unicode="&#xf048;" horiz-adv-x="1024" 
+d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 10 13 19z" />
+    <glyph glyph-name="fast_backward" unicode="&#xf049;" horiz-adv-x="1792" 
+d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 10 13 19l710 710
+q19 19 32 13t13 -32v-710q4 10 13 19z" />
+    <glyph glyph-name="backward" unicode="&#xf04a;" horiz-adv-x="1664" 
+d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q4 10 13 19z" />
+    <glyph glyph-name="play" unicode="&#xf04b;" horiz-adv-x="1408" 
+d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
+    <glyph glyph-name="pause" unicode="&#xf04c;" 
+d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="stop" unicode="&#xf04d;" 
+d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="forward" unicode="&#xf04e;" horiz-adv-x="1664" 
+d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q9 -9 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-4 -10 -13 -19z" />
+    <glyph glyph-name="fast_forward" unicode="&#xf050;" horiz-adv-x="1792" 
+d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q9 -9 13 -19v710q0 26 13 32t32 -13l710 -710q9 -9 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-4 -10 -13 -19l-710 -710
+q-19 -19 -32 -13t-13 32v710q-4 -10 -13 -19z" />
+    <glyph glyph-name="step_forward" unicode="&#xf051;" horiz-adv-x="1024" 
+d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q9 -9 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-4 -10 -13 -19z" />
+    <glyph glyph-name="eject" unicode="&#xf052;" horiz-adv-x="1538" 
+d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
+    <glyph glyph-name="chevron_left" unicode="&#xf053;" horiz-adv-x="1280" 
+d="M1171 1235l-531 -531l531 -531q19 -19 19 -45t-19 -45l-166 -166q-19 -19 -45 -19t-45 19l-742 742q-19 19 -19 45t19 45l742 742q19 19 45 19t45 -19l166 -166q19 -19 19 -45t-19 -45z" />
+    <glyph glyph-name="chevron_right" unicode="&#xf054;" horiz-adv-x="1280" 
+d="M1107 659l-742 -742q-19 -19 -45 -19t-45 19l-166 166q-19 19 -19 45t19 45l531 531l-531 531q-19 19 -19 45t19 45l166 166q19 19 45 19t45 -19l742 -742q19 -19 19 -45t-19 -45z" />
+    <glyph glyph-name="plus_sign" unicode="&#xf055;" 
+d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5
+t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="minus_sign" unicode="&#xf056;" 
+d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5
+t103 -385.5z" />
+    <glyph glyph-name="remove_sign" unicode="&#xf057;" 
+d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19
+q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="ok_sign" unicode="&#xf058;" 
+d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103
+t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="question_sign" unicode="&#xf059;" 
+d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59
+q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5
+t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="info_sign" unicode="&#xf05a;" 
+d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23
+t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="screenshot" unicode="&#xf05b;" 
+d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109
+q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143
+q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="remove_circle" unicode="&#xf05c;" 
+d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23
+l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5
+t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="ok_circle" unicode="&#xf05d;" 
+d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198
+t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="ban_circle" unicode="&#xf05e;" 
+d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61
+t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
+    <glyph glyph-name="arrow_left" unicode="&#xf060;" 
+d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5
+t32.5 -90.5z" />
+    <glyph glyph-name="arrow_right" unicode="&#xf061;" 
+d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
+    <glyph glyph-name="arrow_up" unicode="&#xf062;" horiz-adv-x="1664" 
+d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651
+q37 -39 37 -91z" />
+    <glyph glyph-name="arrow_down" unicode="&#xf063;" horiz-adv-x="1664" 
+d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
+    <glyph glyph-name="share_alt" unicode="&#xf064;" horiz-adv-x="1792" 
+d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22
+t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
+    <glyph glyph-name="resize_full" unicode="&#xf065;" 
+d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332
+q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="resize_small" unicode="&#xf066;" 
+d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45
+t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
+    <glyph glyph-name="plus" unicode="&#xf067;" horiz-adv-x="1408" 
+d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="minus" unicode="&#xf068;" horiz-adv-x="1408" 
+d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="asterisk" unicode="&#xf069;" horiz-adv-x="1664" 
+d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154
+q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
+    <glyph glyph-name="exclamation_sign" unicode="&#xf06a;" 
+d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192
+q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
+    <glyph glyph-name="gift" unicode="&#xf06b;" 
+d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320
+q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5
+t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="leaf" unicode="&#xf06c;" horiz-adv-x="1792" 
+d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268
+q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-43 0 -63.5 17.5t-45.5 59.5q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5
+t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
+    <glyph glyph-name="fire" unicode="&#xf06d;" horiz-adv-x="1408" 
+d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1
+q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
+    <glyph glyph-name="eye_open" unicode="&#xf06e;" horiz-adv-x="1792" 
+d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5
+t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
+    <glyph glyph-name="eye_close" unicode="&#xf070;" horiz-adv-x="1792" 
+d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9
+q-106 -189 -316 -567t-315 -566l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5
+q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z
+" />
+    <glyph glyph-name="warning_sign" unicode="&#xf071;" horiz-adv-x="1792" 
+d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185
+q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
+    <glyph glyph-name="plane" unicode="&#xf072;" horiz-adv-x="1408" 
+d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9
+q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
+    <glyph glyph-name="calendar" unicode="&#xf073;" horiz-adv-x="1664" 
+d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z
+M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64
+q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47
+h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="random" unicode="&#xf074;" horiz-adv-x="1792" 
+d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1
+t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5
+v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111
+t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
+    <glyph glyph-name="comment" unicode="&#xf075;" horiz-adv-x="1792" 
+d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281
+q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
+    <glyph glyph-name="magnet" unicode="&#xf076;" 
+d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384
+q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="chevron_up" unicode="&#xf077;" horiz-adv-x="1792" 
+d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
+    <glyph glyph-name="chevron_down" unicode="&#xf078;" horiz-adv-x="1792" 
+d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
+    <glyph glyph-name="retweet" unicode="&#xf079;" horiz-adv-x="1920" 
+d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -10 7 -21
+zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z
+" />
+    <glyph glyph-name="shopping_cart" unicode="&#xf07a;" horiz-adv-x="1664" 
+d="M640 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1536 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1664 1088v-512q0 -24 -16.5 -42.5t-40.5 -21.5l-1044 -122q13 -60 13 -70q0 -16 -24 -64h920q26 0 45 -19t19 -45
+t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 11 8 31.5t16 36t21.5 40t15.5 29.5l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t19.5 -15.5t13 -24.5t8 -26t5.5 -29.5t4.5 -26h1201q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="folder_close" unicode="&#xf07b;" horiz-adv-x="1664" 
+d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
+    <glyph glyph-name="folder_open" unicode="&#xf07c;" horiz-adv-x="1920" 
+d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5
+t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
+    <glyph glyph-name="resize_vertical" unicode="&#xf07d;" horiz-adv-x="768" 
+d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
+    <glyph glyph-name="resize_horizontal" unicode="&#xf07e;" horiz-adv-x="1792" 
+d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
+    <glyph glyph-name="bar_chart" unicode="&#xf080;" horiz-adv-x="2048" 
+d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
+    <glyph glyph-name="twitter_sign" unicode="&#xf081;" 
+d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4
+q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5
+t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="facebook_sign" unicode="&#xf082;" 
+d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-188v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-532q-119 0 -203.5 84.5t-84.5 203.5v960
+q0 119 84.5 203.5t203.5 84.5h960z" />
+    <glyph glyph-name="camera_retro" unicode="&#xf083;" horiz-adv-x="1792" 
+d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5
+t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280
+q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
+    <glyph glyph-name="key" unicode="&#xf084;" horiz-adv-x="1792" 
+d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26
+l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5
+t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
+    <glyph glyph-name="cogs" unicode="&#xf085;" horiz-adv-x="1920" 
+d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5
+t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -11 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5
+l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7
+l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -8 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31
+q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20
+t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68
+q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70
+q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
+    <glyph glyph-name="comments" unicode="&#xf086;" horiz-adv-x="1792" 
+d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224
+q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7
+q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
+    <glyph glyph-name="thumbs_up_alt" unicode="&#xf087;" 
+d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5
+t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769
+q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128
+q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
+    <glyph glyph-name="thumbs_down_alt" unicode="&#xf088;" 
+d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 31 18 69q0 37 -17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5
+t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z
+M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5
+h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -73 49 -163z" />
+    <glyph glyph-name="star_half" unicode="&#xf089;" horiz-adv-x="896" 
+d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
+    <glyph glyph-name="heart_empty" unicode="&#xf08a;" horiz-adv-x="1792" 
+d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559
+q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5
+q224 0 351 -124t127 -344z" />
+    <glyph glyph-name="signout" unicode="&#xf08b;" horiz-adv-x="1664" 
+d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704
+q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
+    <glyph glyph-name="linkedin_sign" unicode="&#xf08c;" 
+d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5
+q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="pushpin" unicode="&#xf08d;" horiz-adv-x="1152" 
+d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38
+t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
+    <glyph glyph-name="external_link" unicode="&#xf08e;" horiz-adv-x="1792" 
+d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320
+q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="signin" unicode="&#xf090;" 
+d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5
+q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="trophy" unicode="&#xf091;" horiz-adv-x="1664" 
+d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91
+t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96
+q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="github_sign" unicode="&#xf092;" 
+d="M519 336q4 6 -3 13q-9 7 -14 2q-4 -6 3 -13q9 -7 14 -2zM491 377q-5 7 -12 4q-6 -4 0 -12q7 -8 12 -5q6 4 0 13zM450 417q2 4 -5 8q-7 2 -8 -2q-3 -5 4 -8q8 -2 9 2zM471 394q2 1 1.5 4.5t-3.5 5.5q-6 7 -10 3t1 -11q6 -6 11 -2zM557 319q2 7 -9 11q-9 3 -13 -4
+q-2 -7 9 -11q9 -3 13 4zM599 316q0 8 -12 8q-10 0 -10 -8t11 -8t11 8zM638 323q-2 7 -13 5t-9 -9q2 -8 12 -6t10 10zM1280 640q0 212 -150 362t-362 150t-362 -150t-150 -362q0 -167 98 -300.5t252 -185.5q18 -3 26.5 5t8.5 20q0 52 -1 95q-6 -1 -15.5 -2.5t-35.5 -2t-48 4
+t-43.5 20t-29.5 41.5q-23 59 -57 74q-2 1 -4.5 3.5l-8 8t-7 9.5t4 7.5t19.5 3.5q6 0 15 -2t30 -15.5t33 -35.5q16 -28 37.5 -42t43.5 -14t38 3.5t30 9.5q7 47 33 69q-49 6 -86 18.5t-73 39t-55.5 76t-19.5 119.5q0 79 53 137q-24 62 5 136q19 6 54.5 -7.5t60.5 -29.5l26 -16
+q58 17 128 17t128 -17q11 7 28.5 18t55.5 26t57 9q29 -74 5 -136q53 -58 53 -137q0 -57 -14 -100.5t-35.5 -70t-53.5 -44.5t-62.5 -26t-68.5 -12q35 -31 35 -95q0 -40 -0.5 -89t-0.5 -51q0 -12 8.5 -20t26.5 -5q154 52 252 185.5t98 300.5zM1536 1120v-960
+q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="upload_alt" unicode="&#xf093;" horiz-adv-x="1664" 
+d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92
+t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
+    <glyph glyph-name="lemon" unicode="&#xf094;" 
+d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5
+q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44
+q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5
+q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -13 2 -25t3.5 -16.5t7.5 -20.5t8 -20q16 -40 25 -118.5t9 -136.5z" />
+    <glyph glyph-name="phone" unicode="&#xf095;" horiz-adv-x="1408" 
+d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -53 3.5t-57.5 12.5t-47 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-127 79 -264 216t-216 264q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47t-12.5 57.5t-3.5 53q0 92 51 186
+q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174q2 -1 19 -11.5t24 -14
+t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
+    <glyph glyph-name="check_empty" unicode="&#xf096;" horiz-adv-x="1408" 
+d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832
+q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="bookmark_empty" unicode="&#xf097;" horiz-adv-x="1280" 
+d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289
+q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
+    <glyph glyph-name="phone_sign" unicode="&#xf098;" 
+d="M1280 343q0 11 -2 16t-18 16.5t-40.5 25t-47.5 26.5t-45.5 25t-28.5 15q-5 3 -19 13t-25 15t-21 5q-15 0 -36.5 -20.5t-39.5 -45t-38.5 -45t-33.5 -20.5q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170 126.5t-127 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5
+t-3.5 16.5q0 13 20.5 33.5t45 38.5t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5
+t320.5 -216.5q6 -2 30 -11t33 -12.5t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z
+" />
+    <glyph glyph-name="twitter" unicode="&#xf099;" horiz-adv-x="1664" 
+d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41
+q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
+    <glyph glyph-name="facebook" unicode="&#xf09a;" horiz-adv-x="1024" 
+d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
+    <glyph glyph-name="github" unicode="&#xf09b;" 
+d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -40 7t-13 30q0 3 0.5 76.5t0.5 134.5q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 119 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24
+q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-85 13.5q-45 -113 -8 -204q-79 -87 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-39 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5
+t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -88.5t0.5 -54.5q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103zM291 305q3 7 -7 12
+q-10 3 -13 -2q-3 -7 7 -12q9 -6 13 2zM322 271q7 5 -2 16q-10 9 -16 3q-7 -5 2 -16q10 -10 16 -3zM352 226q9 7 0 19q-8 13 -17 6q-9 -5 0 -18t17 -7zM394 184q8 8 -4 19q-12 12 -20 3q-9 -8 4 -19q12 -12 20 -3zM451 159q3 11 -13 16q-15 4 -19 -7t13 -15q15 -6 19 6z
+M514 154q0 13 -17 11q-16 0 -16 -11q0 -13 17 -11q16 0 16 11zM572 164q-2 11 -18 9q-16 -3 -14 -15t18 -8t14 14z" />
+    <glyph glyph-name="unlock" unicode="&#xf09c;" horiz-adv-x="1664" 
+d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5
+t316.5 -131.5t131.5 -316.5z" />
+    <glyph glyph-name="credit_card" unicode="&#xf09d;" horiz-adv-x="1920" 
+d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608
+q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
+    <glyph glyph-name="rss" unicode="&#xf09e;" horiz-adv-x="1408" 
+d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5
+t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294
+q187 -186 294 -425.5t120 -501.5z" />
+    <glyph glyph-name="hdd" unicode="&#xf0a0;" 
+d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5
+h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75
+l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
+    <glyph glyph-name="bullhorn" unicode="&#xf0a1;" horiz-adv-x="1792" 
+d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5
+t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
+    <glyph glyph-name="bell" unicode="&#xf0a2;" horiz-adv-x="1792" 
+d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM246 128h1300q-266 300 -266 832q0 51 -24 105t-69 103t-121.5 80.5t-169.5 31.5t-169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -532 -266 -832z
+M1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5
+t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
+    <glyph glyph-name="certificate" unicode="&#xf0a3;" 
+d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70
+l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70
+l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
+    <glyph glyph-name="hand_right" unicode="&#xf0a4;" horiz-adv-x="1792" 
+d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106
+q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43
+q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5
+t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
+    <glyph glyph-name="hand_left" unicode="&#xf0a5;" horiz-adv-x="1792" 
+d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-8 9 -12 14q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576q-50 0 -89 -38.5
+t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45z
+M1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128q0 122 81.5 189t206.5 67
+q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
+    <glyph glyph-name="hand_up" unicode="&#xf0a6;" 
+d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576
+q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5
+t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76
+q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
+    <glyph glyph-name="hand_down" unicode="&#xf0a7;" 
+d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33
+t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580
+q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100
+q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
+    <glyph glyph-name="circle_arrow_left" unicode="&#xf0a8;" 
+d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="circle_arrow_right" unicode="&#xf0a9;" 
+d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="circle_arrow_up" unicode="&#xf0aa;" 
+d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="circle_arrow_down" unicode="&#xf0ab;" 
+d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="globe" unicode="&#xf0ac;" 
+d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11
+q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 11t-9.5 10q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5
+q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5
+q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5
+t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-4 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3
+q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25
+q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5
+t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5
+t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10.5t17 -19.5q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21
+q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5
+q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3
+q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5
+t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q8 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5
+q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7
+q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
+    <glyph glyph-name="wrench" unicode="&#xf0ad;" horiz-adv-x="1664" 
+d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5
+t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
+    <glyph glyph-name="tasks" unicode="&#xf0ae;" horiz-adv-x="1792" 
+d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19
+t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="filter" unicode="&#xf0b0;" horiz-adv-x="1408" 
+d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
+    <glyph glyph-name="briefcase" unicode="&#xf0b1;" horiz-adv-x="1792" 
+d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68
+t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="fullscreen" unicode="&#xf0b2;" 
+d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144
+l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z
+" />
+    <glyph glyph-name="group" unicode="&#xf0c0;" horiz-adv-x="1920" 
+d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5
+t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75
+t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5
+t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
+    <glyph glyph-name="link" unicode="&#xf0c1;" horiz-adv-x="1664" 
+d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26
+l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15
+t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207
+q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
+    <glyph glyph-name="cloud" unicode="&#xf0c2;" horiz-adv-x="1920" 
+d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z
+" />
+    <glyph glyph-name="beaker" unicode="&#xf0c3;" horiz-adv-x="1664" 
+d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
+    <glyph glyph-name="cut" unicode="&#xf0c4;" horiz-adv-x="1792" 
+d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84
+q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148
+q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108
+q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6
+q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
+    <glyph glyph-name="copy" unicode="&#xf0c5;" horiz-adv-x="1792" 
+d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299
+h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
+    <glyph glyph-name="paper_clip" unicode="&#xf0c6;" horiz-adv-x="1408" 
+d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181
+l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235
+z" />
+    <glyph glyph-name="save" unicode="&#xf0c7;" 
+d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5
+h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
+    <glyph glyph-name="sign_blank" unicode="&#xf0c8;" 
+d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="reorder" unicode="&#xf0c9;" 
+d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45
+t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="ul" unicode="&#xf0ca;" horiz-adv-x="1792" 
+d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5
+t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z
+M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
+    <glyph glyph-name="ol" unicode="&#xf0cb;" horiz-adv-x="1792" 
+d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362
+q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5
+t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 121.5t0.5 121.5v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216
+q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
+    <glyph glyph-name="strikethrough" unicode="&#xf0cc;" horiz-adv-x="1792" 
+d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 98 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6
+l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -56 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23
+l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
+    <glyph glyph-name="underline" unicode="&#xf0cd;" 
+d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47
+q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41
+q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472
+q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
+    <glyph glyph-name="table" unicode="&#xf0ce;" horiz-adv-x="1664" 
+d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23
+v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192
+q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192
+q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113
+z" />
+    <glyph glyph-name="magic" unicode="&#xf0d0;" horiz-adv-x="1664" 
+d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276
+l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
+    <glyph glyph-name="truck" unicode="&#xf0d1;" horiz-adv-x="1792" 
+d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5
+t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38
+t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="pinterest" unicode="&#xf0d2;" 
+d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134
+q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33
+q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="pinterest_sign" unicode="&#xf0d3;" 
+d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5
+t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5
+t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
+    <glyph glyph-name="google_plus_sign" unicode="&#xf0d4;" 
+d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585
+h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="google_plus" unicode="&#xf0d5;" horiz-adv-x="2304" 
+d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62
+q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" />
+    <glyph glyph-name="money" unicode="&#xf0d6;" horiz-adv-x="1920" 
+d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384
+v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="caret_down" unicode="&#xf0d7;" horiz-adv-x="1024" 
+d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="caret_up" unicode="&#xf0d8;" horiz-adv-x="1024" 
+d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+    <glyph glyph-name="caret_left" unicode="&#xf0d9;" horiz-adv-x="640" 
+d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
+    <glyph glyph-name="caret_right" unicode="&#xf0da;" horiz-adv-x="640" 
+d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
+    <glyph glyph-name="columns" unicode="&#xf0db;" horiz-adv-x="1664" 
+d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="sort" unicode="&#xf0dc;" horiz-adv-x="1024" 
+d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+    <glyph glyph-name="sort_down" unicode="&#xf0dd;" horiz-adv-x="1024" 
+d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="sort_up" unicode="&#xf0de;" horiz-adv-x="1024" 
+d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
+    <glyph glyph-name="envelope_alt" unicode="&#xf0e0;" horiz-adv-x="1792" 
+d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123
+q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
+    <glyph glyph-name="linkedin" unicode="&#xf0e1;" 
+d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329
+q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
+    <glyph glyph-name="undo" unicode="&#xf0e2;" 
+d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5
+t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
+    <glyph glyph-name="legal" unicode="&#xf0e3;" horiz-adv-x="1792" 
+d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5
+t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14
+q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28
+q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
+    <glyph glyph-name="dashboard" unicode="&#xf0e4;" horiz-adv-x="1792" 
+d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5
+t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5
+t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29
+q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="comment_alt" unicode="&#xf0e5;" horiz-adv-x="1792" 
+d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640
+q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5
+t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+    <glyph glyph-name="comments_alt" unicode="&#xf0e6;" horiz-adv-x="1792" 
+d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257
+t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5
+t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129
+q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
+    <glyph glyph-name="bolt" unicode="&#xf0e7;" horiz-adv-x="896" 
+d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
+    <glyph glyph-name="sitemap" unicode="&#xf0e8;" horiz-adv-x="1792" 
+d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320
+q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68
+z" />
+    <glyph glyph-name="umbrella" unicode="&#xf0e9;" horiz-adv-x="1664" 
+d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97
+q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69
+q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
+    <glyph glyph-name="paste" unicode="&#xf0ea;" horiz-adv-x="1792" 
+d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28
+h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
+    <glyph glyph-name="light_bulb" unicode="&#xf0eb;" horiz-adv-x="1024" 
+d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134
+q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47
+q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5
+t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
+    <glyph glyph-name="exchange" unicode="&#xf0ec;" horiz-adv-x="1792" 
+d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9
+q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
+    <glyph glyph-name="cloud_download" unicode="&#xf0ed;" horiz-adv-x="1920" 
+d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088
+q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
+    <glyph glyph-name="cloud_upload" unicode="&#xf0ee;" horiz-adv-x="1920" 
+d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088
+q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
+    <glyph glyph-name="user_md" unicode="&#xf0f0;" horiz-adv-x="1408" 
+d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56
+t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68
+t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5
+t271.5 -112.5t112.5 -271.5z" />
+    <glyph glyph-name="stethoscope" unicode="&#xf0f1;" horiz-adv-x="1408" 
+d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48
+t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252
+t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
+    <glyph glyph-name="suitcase" unicode="&#xf0f2;" horiz-adv-x="1792" 
+d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66
+t66 -158z" />
+    <glyph glyph-name="bell_alt" unicode="&#xf0f3;" horiz-adv-x="1792" 
+d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5
+t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
+    <glyph glyph-name="coffee" unicode="&#xf0f4;" horiz-adv-x="1920" 
+d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45
+t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
+    <glyph glyph-name="food" unicode="&#xf0f5;" horiz-adv-x="1408" 
+d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45
+t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="file_text_alt" unicode="&#xf0f6;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M384 736q0 14 9 23t23 9h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64zM1120 512q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704zM1120 256q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704
+q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704z" />
+    <glyph glyph-name="building" unicode="&#xf0f7;" horiz-adv-x="1408" 
+d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="hospital" unicode="&#xf0f8;" horiz-adv-x="1408" 
+d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z
+M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5
+t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320
+v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="ambulance" unicode="&#xf0f9;" horiz-adv-x="1920" 
+d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5
+t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152
+q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="medkit" unicode="&#xf0fa;" horiz-adv-x="1792" 
+d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32
+q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
+    <glyph glyph-name="fighter_jet" unicode="&#xf0fb;" horiz-adv-x="1920" 
+d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96
+q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q128 -28 200 -52t80 -34z" />
+    <glyph glyph-name="beer" unicode="&#xf0fc;" horiz-adv-x="1664" 
+d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
+    <glyph glyph-name="h_sign" unicode="&#xf0fd;" 
+d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960
+q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="f0fe" unicode="&#xf0fe;" 
+d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960
+q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="double_angle_left" unicode="&#xf100;" horiz-adv-x="1024" 
+d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23
+t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
+    <glyph glyph-name="double_angle_right" unicode="&#xf101;" horiz-adv-x="1024" 
+d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23
+l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+    <glyph glyph-name="double_angle_up" unicode="&#xf102;" horiz-adv-x="1152" 
+d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393
+q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+    <glyph glyph-name="double_angle_down" unicode="&#xf103;" horiz-adv-x="1152" 
+d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23
+t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+    <glyph glyph-name="angle_left" unicode="&#xf104;" horiz-adv-x="640" 
+d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+    <glyph glyph-name="angle_right" unicode="&#xf105;" horiz-adv-x="640" 
+d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+    <glyph glyph-name="angle_up" unicode="&#xf106;" horiz-adv-x="1152" 
+d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
+    <glyph glyph-name="angle_down" unicode="&#xf107;" horiz-adv-x="1152" 
+d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
+    <glyph glyph-name="desktop" unicode="&#xf108;" horiz-adv-x="1920" 
+d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19
+t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="laptop" unicode="&#xf109;" horiz-adv-x="1920" 
+d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z
+M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
+    <glyph glyph-name="tablet" unicode="&#xf10a;" horiz-adv-x="1152" 
+d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832
+q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="mobile_phone" unicode="&#xf10b;" horiz-adv-x="768" 
+d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136
+q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="circle_blank" unicode="&#xf10c;" 
+d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103
+t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="quote_left" unicode="&#xf10d;" horiz-adv-x="1664" 
+d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z
+M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
+    <glyph glyph-name="quote_right" unicode="&#xf10e;" horiz-adv-x="1664" 
+d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216
+v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
+    <glyph glyph-name="spinner" unicode="&#xf110;" horiz-adv-x="1792" 
+d="M526 142q0 -53 -37.5 -90.5t-90.5 -37.5q-52 0 -90 38t-38 90q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 -64q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -53 -37.5 -90.5t-90.5 -37.5
+t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1522 142q0 -52 -38 -90t-90 -38q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM558 1138q0 -66 -47 -113t-113 -47t-113 47t-47 113t47 113t113 47t113 -47t47 -113z
+M1728 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1088 1344q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1618 1138q0 -93 -66 -158.5t-158 -65.5q-93 0 -158.5 65.5t-65.5 158.5
+q0 92 65.5 158t158.5 66q92 0 158 -66t66 -158z" />
+    <glyph glyph-name="circle" unicode="&#xf111;" 
+d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="reply" unicode="&#xf112;" horiz-adv-x="1792" 
+d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19
+l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
+    <glyph glyph-name="github_alt" unicode="&#xf113;" horiz-adv-x="1664" 
+d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320
+q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86
+t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218
+q0 -87 -27 -168q136 -160 136 -398z" />
+    <glyph glyph-name="folder_close_alt" unicode="&#xf114;" horiz-adv-x="1664" 
+d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320
+q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
+    <glyph glyph-name="folder_open_alt" unicode="&#xf115;" horiz-adv-x="1920" 
+d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68
+v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z
+" />
+    <glyph glyph-name="expand_alt" unicode="&#xf116;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="collapse_alt" unicode="&#xf117;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="smile" unicode="&#xf118;" 
+d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5
+t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5
+t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="frown" unicode="&#xf119;" 
+d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5
+t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204
+t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="meh" unicode="&#xf11a;" 
+d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5
+t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="gamepad" unicode="&#xf11b;" horiz-adv-x="1920" 
+d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5
+t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150
+t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
+    <glyph glyph-name="keyboard" unicode="&#xf11c;" horiz-adv-x="1920" 
+d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16
+h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16
+h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96
+q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896
+h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
+    <glyph glyph-name="flag_alt" unicode="&#xf11d;" horiz-adv-x="1792" 
+d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9
+h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102
+q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
+    <glyph glyph-name="flag_checkered" unicode="&#xf11e;" horiz-adv-x="1792" 
+d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2
+q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266
+q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8
+q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
+    <glyph glyph-name="terminal" unicode="&#xf120;" horiz-adv-x="1664" 
+d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9
+t9 -23z" />
+    <glyph glyph-name="code" unicode="&#xf121;" horiz-adv-x="1920" 
+d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5
+l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
+    <glyph glyph-name="reply_all" unicode="&#xf122;" horiz-adv-x="1792" 
+d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1
+q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
+    <glyph glyph-name="star_half_empty" unicode="&#xf123;" horiz-adv-x="1664" 
+d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5
+l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
+    <glyph glyph-name="location_arrow" unicode="&#xf124;" horiz-adv-x="1408" 
+d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
+    <glyph glyph-name="crop" unicode="&#xf125;" horiz-adv-x="1664" 
+d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23
+v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="code_fork" unicode="&#xf126;" horiz-adv-x="1024" 
+d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5
+q-2 -287 -226 -414q-67 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497
+q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
+    <glyph glyph-name="unlink" unicode="&#xf127;" horiz-adv-x="1664" 
+d="M439 265l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320
+q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18
+l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9
+t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
+    <glyph glyph-name="question" unicode="&#xf128;" horiz-adv-x="1024" 
+d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5
+t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
+    <glyph glyph-name="_279" unicode="&#xf129;" horiz-adv-x="640" 
+d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192
+q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="exclamation" unicode="&#xf12a;" horiz-adv-x="640" 
+d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
+    <glyph glyph-name="superscript" unicode="&#xf12b;" 
+d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3q-1 -3 -2.5 -6.5t-3.5 -8t-3 -6.5q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109z
+M1534 846v-206h-514l-3 27q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5
+t-65.5 -51.5t-30.5 -63h232v80h126z" />
+    <glyph glyph-name="subscript" unicode="&#xf12c;" 
+d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3q-1 -3 -2.5 -6.5t-3.5 -8t-3 -6.5q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109z
+M1536 -50v-206h-514l-4 27q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73
+h232v80h126z" />
+    <glyph glyph-name="_283" unicode="&#xf12d;" horiz-adv-x="1920" 
+d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
+    <glyph glyph-name="puzzle_piece" unicode="&#xf12e;" horiz-adv-x="1664" 
+d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5
+t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89
+q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117
+q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
+    <glyph glyph-name="microphone" unicode="&#xf130;" horiz-adv-x="1152" 
+d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5
+t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
+    <glyph glyph-name="microphone_off" unicode="&#xf131;" horiz-adv-x="1408" 
+d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128
+q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23
+t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
+    <glyph glyph-name="shield" unicode="&#xf132;" horiz-adv-x="1280" 
+d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150
+t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="calendar_empty" unicode="&#xf133;" horiz-adv-x="1664" 
+d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280
+q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="fire_extinguisher" unicode="&#xf134;" horiz-adv-x="1408" 
+d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800
+q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113
+q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
+    <glyph glyph-name="rocket" unicode="&#xf135;" horiz-adv-x="1664" 
+d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1
+q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
+    <glyph glyph-name="maxcdn" unicode="&#xf136;" horiz-adv-x="1792" 
+d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" />
+    <glyph glyph-name="chevron_sign_left" unicode="&#xf137;" 
+d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5
+t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="chevron_sign_right" unicode="&#xf138;" 
+d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5
+t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="chevron_sign_up" unicode="&#xf139;" 
+d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5
+t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="chevron_sign_down" unicode="&#xf13a;" 
+d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5
+t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="html5" unicode="&#xf13b;" horiz-adv-x="1408" 
+d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
+    <glyph glyph-name="css3" unicode="&#xf13c;" horiz-adv-x="1792" 
+d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
+    <glyph glyph-name="anchor" unicode="&#xf13d;" horiz-adv-x="1792" 
+d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352
+q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19
+t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="unlock_alt" unicode="&#xf13e;" horiz-adv-x="1152" 
+d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181
+v-320h736z" />
+    <glyph glyph-name="bullseye" unicode="&#xf140;" 
+d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150
+t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640
+q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="ellipsis_horizontal" unicode="&#xf141;" horiz-adv-x="1408" 
+d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192
+q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="ellipsis_vertical" unicode="&#xf142;" horiz-adv-x="384" 
+d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192
+q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
+    <glyph glyph-name="_303" unicode="&#xf143;" 
+d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 233 -176.5 396.5t-396.5 176.5q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128
+q13 0 23 10t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960
+q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="play_sign" unicode="&#xf144;" 
+d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56
+q16 -8 32 -8q17 0 32 9z" />
+    <glyph glyph-name="ticket" unicode="&#xf145;" horiz-adv-x="1792" 
+d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136
+t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
+    <glyph glyph-name="minus_sign_alt" unicode="&#xf146;" 
+d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5
+t84.5 -203.5z" />
+    <glyph glyph-name="check_minus" unicode="&#xf147;" horiz-adv-x="1408" 
+d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5
+t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="level_up" unicode="&#xf148;" horiz-adv-x="1024" 
+d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
+    <glyph glyph-name="level_down" unicode="&#xf149;" horiz-adv-x="1024" 
+d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
+    <glyph glyph-name="check_sign" unicode="&#xf14a;" 
+d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5
+t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="edit_sign" unicode="&#xf14b;" 
+d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120
+v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_312" unicode="&#xf14c;" 
+d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960
+q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="share_sign" unicode="&#xf14d;" 
+d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q11 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5
+t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="compass" unicode="&#xf14e;" 
+d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103
+t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="collapse" unicode="&#xf150;" 
+d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120
+v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="collapse_top" unicode="&#xf151;" 
+d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960
+q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_317" unicode="&#xf152;" 
+d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5
+t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="eur" unicode="&#xf153;" horiz-adv-x="1024" 
+d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9
+t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26
+l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
+    <glyph glyph-name="gbp" unicode="&#xf154;" horiz-adv-x="1024" 
+d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7
+q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
+    <glyph glyph-name="usd" unicode="&#xf155;" horiz-adv-x="1024" 
+d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43
+t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5
+t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50
+t53 -63.5t31.5 -76.5t13 -94z" />
+    <glyph glyph-name="inr" unicode="&#xf156;" horiz-adv-x="898" 
+d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102
+q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="jpy" unicode="&#xf157;" horiz-adv-x="1027" 
+d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61
+l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
+    <glyph glyph-name="rub" unicode="&#xf158;" horiz-adv-x="1280" 
+d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128
+q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" />
+    <glyph glyph-name="krw" unicode="&#xf159;" horiz-adv-x="1792" 
+d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23
+t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28
+q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="btc" unicode="&#xf15a;" horiz-adv-x="1280" 
+d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164
+l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30
+t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
+    <glyph glyph-name="file" unicode="&#xf15b;" 
+d="M1024 1024v472q22 -14 36 -28l408 -408q14 -14 28 -36h-472zM896 992q0 -40 28 -68t68 -28h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544z" />
+    <glyph glyph-name="file_text" unicode="&#xf15c;" 
+d="M1468 1060q14 -14 28 -36h-472v472q22 -14 36 -28zM992 896h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544q0 -40 28 -68t68 -28zM1152 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704
+q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23z" />
+    <glyph glyph-name="sort_by_alphabet" unicode="&#xf15d;" horiz-adv-x="1664" 
+d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23
+v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162
+l230 -662h70z" />
+    <glyph glyph-name="_329" unicode="&#xf15e;" horiz-adv-x="1664" 
+d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150
+v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248
+v119h121z" />
+    <glyph glyph-name="sort_by_attributes" unicode="&#xf160;" horiz-adv-x="1792" 
+d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832
+q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256
+q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="sort_by_attributes_alt" unicode="&#xf161;" horiz-adv-x="1792" 
+d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192
+q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832
+q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="sort_by_order" unicode="&#xf162;" 
+d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23
+zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5
+t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
+    <glyph glyph-name="sort_by_order_alt" unicode="&#xf163;" 
+d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9
+t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13
+q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
+    <glyph glyph-name="_334" unicode="&#xf164;" horiz-adv-x="1664" 
+d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76
+q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5
+t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
+    <glyph glyph-name="_335" unicode="&#xf165;" horiz-adv-x="1664" 
+d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135
+t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121
+t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
+    <glyph glyph-name="youtube_sign" unicode="&#xf166;" 
+d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 17 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15
+q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38
+q21 -29 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5
+q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78q7 -23 23 -69l24 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38
+q-51 0 -78 -38q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5
+h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="youtube" unicode="&#xf167;" 
+d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73
+q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51
+q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99
+q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-38 -51 -106 -51q-67 0 -105 51
+q-28 38 -28 118v175q0 80 28 117q38 51 105 51q68 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
+    <glyph glyph-name="xing" unicode="&#xf168;" horiz-adv-x="1408" 
+d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942
+q25 45 64 45h241q22 0 31 -15z" />
+    <glyph glyph-name="xing_sign" unicode="&#xf169;" 
+d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1
+l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="youtube_play" unicode="&#xf16a;" horiz-adv-x="1792" 
+d="M711 408l484 250l-484 253v-503zM896 1270q168 0 324.5 -4.5t229.5 -9.5l73 -4q1 0 17 -1.5t23 -3t23.5 -4.5t28.5 -8t28 -13t31 -19.5t29 -26.5q6 -6 15.5 -18.5t29 -58.5t26.5 -101q8 -64 12.5 -136.5t5.5 -113.5v-40v-136q1 -145 -18 -290q-7 -55 -25 -99.5t-32 -61.5
+l-14 -17q-14 -15 -29 -26.5t-31 -19t-28 -12.5t-28.5 -8t-24 -4.5t-23 -3t-16.5 -1.5q-251 -19 -627 -19q-207 2 -359.5 6.5t-200.5 7.5l-49 4l-36 4q-36 5 -54.5 10t-51 21t-56.5 41q-6 6 -15.5 18.5t-29 58.5t-26.5 101q-8 64 -12.5 136.5t-5.5 113.5v40v136
+q-1 145 18 290q7 55 25 99.5t32 61.5l14 17q14 15 29 26.5t31 19.5t28 13t28.5 8t23.5 4.5t23 3t17 1.5q251 18 627 18z" />
+    <glyph glyph-name="dropbox" unicode="&#xf16b;" horiz-adv-x="1792" 
+d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
+    <glyph glyph-name="stackexchange" unicode="&#xf16c;" 
+d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
+    <glyph glyph-name="instagram" unicode="&#xf16d;" 
+d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1162 640q0 -164 -115 -279t-279 -115t-279 115t-115 279t115 279t279 115t279 -115t115 -279zM1270 1050q0 -38 -27 -65t-65 -27t-65 27t-27 65t27 65t65 27t65 -27t27 -65zM768 1270
+q-7 0 -76.5 0.5t-105.5 0t-96.5 -3t-103 -10t-71.5 -18.5q-50 -20 -88 -58t-58 -88q-11 -29 -18.5 -71.5t-10 -103t-3 -96.5t0 -105.5t0.5 -76.5t-0.5 -76.5t0 -105.5t3 -96.5t10 -103t18.5 -71.5q20 -50 58 -88t88 -58q29 -11 71.5 -18.5t103 -10t96.5 -3t105.5 0t76.5 0.5
+t76.5 -0.5t105.5 0t96.5 3t103 10t71.5 18.5q50 20 88 58t58 88q11 29 18.5 71.5t10 103t3 96.5t0 105.5t-0.5 76.5t0.5 76.5t0 105.5t-3 96.5t-10 103t-18.5 71.5q-20 50 -58 88t-88 58q-29 11 -71.5 18.5t-103 10t-96.5 3t-105.5 0t-76.5 -0.5zM1536 640q0 -229 -5 -317
+q-10 -208 -124 -322t-322 -124q-88 -5 -317 -5t-317 5q-208 10 -322 124t-124 322q-5 88 -5 317t5 317q10 208 124 322t322 124q88 5 317 5t317 -5q208 -10 322 -124t124 -322q5 -88 5 -317z" />
+    <glyph glyph-name="flickr" unicode="&#xf16e;" 
+d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150
+t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
+    <glyph glyph-name="adn" unicode="&#xf170;" 
+d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="f171" unicode="&#xf171;" horiz-adv-x="1408" 
+d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22
+t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18
+t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5
+t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
+    <glyph glyph-name="bitbucket_sign" unicode="&#xf172;" 
+d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5
+t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z
+M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120
+v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="tumblr" unicode="&#xf173;" horiz-adv-x="1024" 
+d="M944 207l80 -237q-23 -35 -111 -66t-177 -32q-104 -2 -190.5 26t-142.5 74t-95 106t-55.5 120t-16.5 118v544h-168v215q72 26 129 69.5t91 90t58 102t34 99t15 88.5q1 5 4.5 8.5t7.5 3.5h244v-424h333v-252h-334v-518q0 -30 6.5 -56t22.5 -52.5t49.5 -41.5t81.5 -14
+q78 2 134 29z" />
+    <glyph glyph-name="tumblr_sign" unicode="&#xf174;" 
+d="M1136 75l-62 183q-44 -22 -103 -22q-36 -1 -62 10.5t-38.5 31.5t-17.5 40.5t-5 43.5v398h257v194h-256v326h-188q-8 0 -9 -10q-5 -44 -17.5 -87t-39 -95t-77 -95t-118.5 -68v-165h130v-418q0 -57 21.5 -115t65 -111t121 -85.5t176.5 -30.5q69 1 136.5 25t85.5 50z
+M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="long_arrow_down" unicode="&#xf175;" horiz-adv-x="768" 
+d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
+    <glyph glyph-name="long_arrow_up" unicode="&#xf176;" horiz-adv-x="768" 
+d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
+    <glyph glyph-name="long_arrow_left" unicode="&#xf177;" horiz-adv-x="1792" 
+d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="long_arrow_right" unicode="&#xf178;" horiz-adv-x="1792" 
+d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
+    <glyph glyph-name="apple" unicode="&#xf179;" horiz-adv-x="1408" 
+d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q113 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65
+q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
+    <glyph glyph-name="windows" unicode="&#xf17a;" horiz-adv-x="1664" 
+d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
+    <glyph glyph-name="android" unicode="&#xf17b;" horiz-adv-x="1408" 
+d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30
+t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5
+h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
+    <glyph glyph-name="linux" unicode="&#xf17c;" 
+d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-10 -11 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z
+M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7
+q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15
+q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5
+t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19
+q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63
+q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18q-2 -1 -4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92
+q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152
+q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-6 0 -8 -2t0 -4
+t5 -3q14 -4 18 -31q0 -3 8 2q2 2 2 3zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5
+t-30 -18.5t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43
+q-19 4 -51 9.5t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49
+t-14 -48q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54
+q110 143 124 195q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5
+t-40.5 -33.5t-61 -14q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5
+t15.5 47.5q1 -31 8 -56.5t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
+    <glyph glyph-name="dribble" unicode="&#xf17d;" 
+d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81
+t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19
+q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -5 6.5 -17t7.5 -17q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6
+t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="skype" unicode="&#xf17e;" 
+d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5
+t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5
+q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80
+q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
+    <glyph glyph-name="foursquare" unicode="&#xf180;" horiz-adv-x="1280" 
+d="M1000 1102l37 194q5 23 -9 40t-35 17h-712q-23 0 -38.5 -17t-15.5 -37v-1101q0 -7 6 -1l291 352q23 26 38 33.5t48 7.5h239q22 0 37 14.5t18 29.5q24 130 37 191q4 21 -11.5 40t-36.5 19h-294q-29 0 -48 19t-19 48v42q0 29 19 47.5t48 18.5h346q18 0 35 13.5t20 29.5z
+M1227 1324q-15 -73 -53.5 -266.5t-69.5 -350t-35 -173.5q-6 -22 -9 -32.5t-14 -32.5t-24.5 -33t-38.5 -21t-58 -10h-271q-13 0 -22 -10q-8 -9 -426 -494q-22 -25 -58.5 -28.5t-48.5 5.5q-55 22 -55 98v1410q0 55 38 102.5t120 47.5h888q95 0 127 -53t10 -159zM1227 1324
+l-158 -790q4 17 35 173.5t69.5 350t53.5 266.5z" />
+    <glyph glyph-name="trello" unicode="&#xf181;" 
+d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408
+q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="female" unicode="&#xf182;" horiz-adv-x="1280" 
+d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43
+q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+    <glyph glyph-name="male" unicode="&#xf183;" horiz-adv-x="1024" 
+d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z
+M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+    <glyph glyph-name="gittip" unicode="&#xf184;" 
+d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103
+t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="sun" unicode="&#xf185;" horiz-adv-x="1792" 
+d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4
+l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94
+q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
+    <glyph glyph-name="_366" unicode="&#xf186;" 
+d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61
+t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
+    <glyph glyph-name="archive" unicode="&#xf187;" horiz-adv-x="1792" 
+d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536
+q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="bug" unicode="&#xf188;" horiz-adv-x="1664" 
+d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207
+q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19
+t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
+    <glyph glyph-name="vk" unicode="&#xf189;" horiz-adv-x="1920" 
+d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-40 -51 -55 -72t-30.5 -49.5t-12 -42t13 -34.5t32.5 -43t57 -53q4 -2 5 -4q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58
+t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6
+q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q16 19 38 30q53 26 239 24
+q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2
+q39 5 64 -2.5t31 -16.5z" />
+    <glyph glyph-name="weibo" unicode="&#xf18a;" horiz-adv-x="1792" 
+d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12
+q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422
+q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178
+q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z
+M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
+    <glyph glyph-name="renren" unicode="&#xf18b;" 
+d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495
+q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" />
+    <glyph glyph-name="_372" unicode="&#xf18c;" horiz-adv-x="1408" 
+d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5
+t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56
+t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -4 1 -50t-1 -72q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5
+t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" />
+    <glyph glyph-name="stack_exchange" unicode="&#xf18d;" horiz-adv-x="1280" 
+d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z
+" />
+    <glyph glyph-name="_374" unicode="&#xf18e;" 
+d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198
+t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="arrow_circle_alt_left" unicode="&#xf190;" 
+d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198
+t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_376" unicode="&#xf191;" 
+d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z
+M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="dot_circle_alt" unicode="&#xf192;" 
+d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5
+t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_378" unicode="&#xf193;" horiz-adv-x="1664" 
+d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128
+q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 17 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
+    <glyph glyph-name="vimeo_square" unicode="&#xf194;" 
+d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179
+q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_380" unicode="&#xf195;" horiz-adv-x="1152" 
+d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160
+q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="plus_square_o" unicode="&#xf196;" horiz-adv-x="1408" 
+d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832
+q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_382" unicode="&#xf197;" horiz-adv-x="2176" 
+d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40
+t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29
+q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
+    <glyph glyph-name="_383" unicode="&#xf198;" horiz-adv-x="1664" 
+d="M1519 760q62 0 103.5 -40.5t41.5 -101.5q0 -97 -93 -130l-172 -59l56 -167q7 -21 7 -47q0 -59 -42 -102t-101 -43q-47 0 -85.5 27t-53.5 72l-55 165l-310 -106l55 -164q8 -24 8 -47q0 -59 -42 -102t-102 -43q-47 0 -85 27t-53 72l-55 163l-153 -53q-29 -9 -50 -9
+q-61 0 -101.5 40t-40.5 101q0 47 27.5 85t71.5 53l156 53l-105 313l-156 -54q-26 -8 -48 -8q-60 0 -101 40.5t-41 100.5q0 47 27.5 85t71.5 53l157 53l-53 159q-8 24 -8 47q0 60 42 102.5t102 42.5q47 0 85 -27t53 -72l54 -160l310 105l-54 160q-8 24 -8 47q0 59 42.5 102
+t101.5 43q47 0 85.5 -27.5t53.5 -71.5l53 -161l162 55q21 6 43 6q60 0 102.5 -39.5t42.5 -98.5q0 -45 -30 -81.5t-74 -51.5l-157 -54l105 -316l164 56q24 8 46 8zM725 498l310 105l-105 315l-310 -107z" />
+    <glyph glyph-name="_384" unicode="&#xf199;" 
+d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM1280 352v436q-31 -35 -64 -55q-34 -22 -132.5 -85t-151.5 -99q-98 -69 -164 -69v0v0q-66 0 -164 69
+q-47 32 -142 92.5t-142 92.5q-12 8 -33 27t-31 27v-436q0 -40 28 -68t68 -28h832q40 0 68 28t28 68zM1280 925q0 41 -27.5 70t-68.5 29h-832q-40 0 -68 -28t-28 -68q0 -37 30.5 -76.5t67.5 -64.5q47 -32 137.5 -89t129.5 -83q3 -2 17 -11.5t21 -14t21 -13t23.5 -13
+t21.5 -9.5t22.5 -7.5t20.5 -2.5t20.5 2.5t22.5 7.5t21.5 9.5t23.5 13t21 13t21 14t17 11.5l267 174q35 23 66.5 62.5t31.5 73.5z" />
+    <glyph glyph-name="_385" unicode="&#xf19a;" horiz-adv-x="1792" 
+d="M127 640q0 163 67 313l367 -1005q-196 95 -315 281t-119 411zM1415 679q0 -19 -2.5 -38.5t-10 -49.5t-11.5 -44t-17.5 -59t-17.5 -58l-76 -256l-278 826q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-75 1 -202 10q-12 1 -20.5 -5t-11.5 -15t-1.5 -18.5t9 -16.5
+t19.5 -8l80 -8l120 -328l-168 -504l-280 832q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-7 0 -23 0.5t-26 0.5q105 160 274.5 253.5t367.5 93.5q147 0 280.5 -53t238.5 -149h-10q-55 0 -92 -40.5t-37 -95.5q0 -12 2 -24t4 -21.5t8 -23t9 -21t12 -22.5t12.5 -21
+t14.5 -24t14 -23q63 -107 63 -212zM909 573l237 -647q1 -6 5 -11q-126 -44 -255 -44q-112 0 -217 32zM1570 1009q95 -174 95 -369q0 -209 -104 -385.5t-279 -278.5l235 678q59 169 59 276q0 42 -6 79zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286
+t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 -215q173 0 331.5 68t273 182.5t182.5 273t68 331.5t-68 331.5t-182.5 273t-273 182.5t-331.5 68t-331.5 -68t-273 -182.5t-182.5 -273t-68 -331.5t68 -331.5t182.5 -273
+t273 -182.5t331.5 -68z" />
+    <glyph glyph-name="_386" unicode="&#xf19b;" horiz-adv-x="1792" 
+d="M1086 1536v-1536l-272 -128q-228 20 -414 102t-293 208.5t-107 272.5q0 140 100.5 263.5t275 205.5t391.5 108v-172q-217 -38 -356.5 -150t-139.5 -255q0 -152 154.5 -267t388.5 -145v1360zM1755 954l37 -390l-525 114l147 83q-119 70 -280 99v172q277 -33 481 -157z" />
+    <glyph glyph-name="_387" unicode="&#xf19c;" horiz-adv-x="2048" 
+d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64
+q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
+    <glyph glyph-name="_388" unicode="&#xf19d;" horiz-adv-x="2304" 
+d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433
+q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
+    <glyph glyph-name="_389" unicode="&#xf19e;" 
+d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q44 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0
+q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
+    <glyph glyph-name="uniF1A0" unicode="&#xf1a0;" 
+d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5
+t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" />
+    <glyph glyph-name="f1a1" unicode="&#xf1a1;" horiz-adv-x="1792" 
+d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26
+t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37
+q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191
+t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="_392" unicode="&#xf1a2;" 
+d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54
+q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83
+q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960
+q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_393" unicode="&#xf1a3;" 
+d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150
+v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103
+t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="f1a4" unicode="&#xf1a4;" horiz-adv-x="1920" 
+d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328
+v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
+    <glyph glyph-name="_395" unicode="&#xf1a5;" 
+d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5
+t84.5 -203.5z" />
+    <glyph glyph-name="_396" unicode="&#xf1a6;" horiz-adv-x="2048" 
+d="M328 1254h204v-983h-532v697h328v286zM328 435v369h-123v-369h123zM614 968v-697h205v697h-205zM614 1254v-204h205v204h-205zM901 968h533v-942h-533v163h328v82h-328v697zM1229 435v369h-123v-369h123zM1516 968h532v-942h-532v163h327v82h-327v697zM1843 435v369h-123
+v-369h123z" />
+    <glyph glyph-name="_397" unicode="&#xf1a7;" 
+d="M1046 516q0 -64 -38 -109t-91 -45q-43 0 -70 15v277q28 17 70 17q53 0 91 -45.5t38 -109.5zM703 944q0 -64 -38 -109.5t-91 -45.5q-43 0 -70 15v277q28 17 70 17q53 0 91 -45t38 -109zM1265 513q0 134 -88 229t-213 95q-20 0 -39 -3q-23 -78 -78 -136q-87 -95 -211 -101
+v-636l211 41v206q51 -19 117 -19q125 0 213 95t88 229zM922 940q0 134 -88.5 229t-213.5 95q-74 0 -141 -36h-186v-840l211 41v206q55 -19 116 -19q125 0 213.5 95t88.5 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960
+q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_398" unicode="&#xf1a8;" horiz-adv-x="2038" 
+d="M1222 607q75 3 143.5 -20.5t118 -58.5t101 -94.5t84 -108t75.5 -120.5q33 -56 78.5 -109t75.5 -80.5t99 -88.5q-48 -30 -108.5 -57.5t-138.5 -59t-114 -47.5q-44 37 -74 115t-43.5 164.5t-33 180.5t-42.5 168.5t-72.5 123t-122.5 48.5l-10 -2l-6 -4q4 -5 13 -14
+q6 -5 28 -23.5t25.5 -22t19 -18t18 -20.5t11.5 -21t10.5 -27.5t4.5 -31t4 -40.5l1 -33q1 -26 -2.5 -57.5t-7.5 -52t-12.5 -58.5t-11.5 -53q-35 1 -101 -9.5t-98 -10.5q-39 0 -72 10q-2 16 -2 47q0 74 3 96q2 13 31.5 41.5t57 59t26.5 51.5q-24 2 -43 -24
+q-36 -53 -111.5 -99.5t-136.5 -46.5q-25 0 -75.5 63t-106.5 139.5t-84 96.5q-6 4 -27 30q-482 -112 -513 -112q-16 0 -28 11t-12 27q0 15 8.5 26.5t22.5 14.5l486 106q-8 14 -8 25t5.5 17.5t16 11.5t20 7t23 4.5t18.5 4.5q4 1 15.5 7.5t17.5 6.5q15 0 28 -16t20 -33
+q163 37 172 37q17 0 29.5 -11t12.5 -28q0 -15 -8.5 -26t-23.5 -14l-182 -40l-1 -16q-1 -26 81.5 -117.5t104.5 -91.5q47 0 119 80t72 129q0 36 -23.5 53t-51 18.5t-51 11.5t-23.5 34q0 16 10 34l-68 19q43 44 43 117q0 26 -5 58q82 16 144 16q44 0 71.5 -1.5t48.5 -8.5
+t31 -13.5t20.5 -24.5t15.5 -33.5t17 -47.5t24 -60l50 25q-3 -40 -23 -60t-42.5 -21t-40 -6.5t-16.5 -20.5zM1282 842q-5 5 -13.5 15.5t-12 14.5t-10.5 11.5t-10 10.5l-8 8t-8.5 7.5t-8 5t-8.5 4.5q-7 3 -14.5 5t-20.5 2.5t-22 0.5h-32.5h-37.5q-126 0 -217 -43
+q16 30 36 46.5t54 29.5t65.5 36t46 36.5t50 55t43.5 50.5q12 -9 28 -31.5t32 -36.5t38 -13l12 1v-76l22 -1q247 95 371 190q28 21 50 39t42.5 37.5t33 31t29.5 34t24 31t24.5 37t23 38t27 47.5t29.5 53l7 9q-2 -53 -43 -139q-79 -165 -205 -264t-306 -142q-14 -3 -42 -7.5
+t-50 -9.5t-39 -14q3 -19 24.5 -46t21.5 -34q0 -11 -26 -30zM1061 -79q39 26 131.5 47.5t146.5 21.5q9 0 22.5 -15.5t28 -42.5t26 -50t24 -51t14.5 -33q-121 -45 -244 -45q-61 0 -125 11zM822 568l48 12l109 -177l-73 -48zM1323 51q3 -15 3 -16q0 -7 -17.5 -14.5t-46 -13
+t-54 -9.5t-53.5 -7.5t-32 -4.5l-7 43q21 2 60.5 8.5t72 10t60.5 3.5h14zM866 679l-96 -20l-6 17q10 1 32.5 7t34.5 6q19 0 35 -10zM1061 45h31l10 -83l-41 -12v95zM1950 1535v1v-1zM1950 1535l-1 -5l-2 -2l1 3zM1950 1535l1 1z" />
+    <glyph glyph-name="_399" unicode="&#xf1a9;" 
+d="M1167 -50q-5 19 -24 5q-30 -22 -87 -39t-131 -17q-129 0 -193 49q-5 4 -13 4q-11 0 -26 -12q-7 -6 -7.5 -16t7.5 -20q34 -32 87.5 -46t102.5 -12.5t99 4.5q41 4 84.5 20.5t65 30t28.5 20.5q12 12 7 29zM1128 65q-19 47 -39 61q-23 15 -76 15q-47 0 -71 -10
+q-29 -12 -78 -56q-26 -24 -12 -44q9 -8 17.5 -4.5t31.5 23.5q3 2 10.5 8.5t10.5 8.5t10 7t11.5 7t12.5 5t15 4.5t16.5 2.5t20.5 1q27 0 44.5 -7.5t23 -14.5t13.5 -22q10 -17 12.5 -20t12.5 1q23 12 14 34zM1483 346q0 22 -5 44.5t-16.5 45t-34 36.5t-52.5 14
+q-33 0 -97 -41.5t-129 -83.5t-101 -42q-27 -1 -63.5 19t-76 49t-83.5 58t-100 49t-111 19q-115 -1 -197 -78.5t-84 -178.5q-2 -112 74 -164q29 -20 62.5 -28.5t103.5 -8.5q57 0 132 32.5t134 71t120 70.5t93 31q26 -1 65 -31.5t71.5 -67t68 -67.5t55.5 -32q35 -3 58.5 14
+t55.5 63q28 41 42.5 101t14.5 106zM1536 506q0 -164 -62 -304.5t-166 -236t-242.5 -149.5t-290.5 -54t-293 57.5t-247.5 157t-170.5 241.5t-64 302q0 89 19.5 172.5t49 145.5t70.5 118.5t78.5 94t78.5 69.5t64.5 46.5t42.5 24.5q14 8 51 26.5t54.5 28.5t48 30t60.5 44
+q36 28 58 72.5t30 125.5q129 -155 186 -193q44 -29 130 -68t129 -66q21 -13 39 -25t60.5 -46.5t76 -70.5t75 -95t69 -122t47 -148.5t19.5 -177.5z" />
+    <glyph glyph-name="_400" unicode="&#xf1aa;" 
+d="M1070 463l-160 -160l-151 -152l-30 -30q-65 -64 -151.5 -87t-171.5 -2q-16 -70 -72 -115t-129 -45q-85 0 -145 60.5t-60 145.5q0 72 44.5 128t113.5 72q-22 86 1 173t88 152l12 12l151 -152l-11 -11q-37 -37 -37 -89t37 -90q37 -37 89 -37t89 37l30 30l151 152l161 160z
+M729 1145l12 -12l-152 -152l-12 12q-37 37 -89 37t-89 -37t-37 -89.5t37 -89.5l29 -29l152 -152l160 -160l-151 -152l-161 160l-151 152l-30 30q-68 67 -90 159.5t5 179.5q-70 15 -115 71t-45 129q0 85 60 145.5t145 60.5q76 0 133.5 -49t69.5 -123q84 20 169.5 -3.5
+t149.5 -87.5zM1536 78q0 -85 -60 -145.5t-145 -60.5q-74 0 -131 47t-71 118q-86 -28 -179.5 -6t-161.5 90l-11 12l151 152l12 -12q37 -37 89 -37t89 37t37 89t-37 89l-30 30l-152 152l-160 160l152 152l160 -160l152 -152l29 -30q64 -64 87.5 -150.5t2.5 -171.5
+q76 -11 126.5 -68.5t50.5 -134.5zM1534 1202q0 -77 -51 -135t-127 -69q26 -85 3 -176.5t-90 -158.5l-12 -12l-151 152l12 12q37 37 37 89t-37 89t-89 37t-89 -37l-30 -30l-152 -152l-160 -160l-152 152l161 160l152 152l29 30q67 67 159 89.5t178 -3.5q11 75 68.5 126
+t135.5 51q85 0 145 -60.5t60 -145.5z" />
+    <glyph glyph-name="f1ab" unicode="&#xf1ab;" 
+d="M654 458q-1 -3 -12.5 0.5t-31.5 11.5l-20 9q-44 20 -87 49q-7 5 -41 31.5t-38 28.5q-67 -103 -134 -181q-81 -95 -105 -110q-4 -2 -19.5 -4t-18.5 0q6 4 82 92q21 24 85.5 115t78.5 118q17 30 51 98.5t36 77.5q-8 1 -110 -33q-8 -2 -27.5 -7.5t-34.5 -9.5t-17 -5
+q-2 -2 -2 -10.5t-1 -9.5q-5 -10 -31 -15q-23 -7 -47 0q-18 4 -28 21q-4 6 -5 23q6 2 24.5 5t29.5 6q58 16 105 32q100 35 102 35q10 2 43 19.5t44 21.5q9 3 21.5 8t14.5 5.5t6 -0.5q2 -12 -1 -33q0 -2 -12.5 -27t-26.5 -53.5t-17 -33.5q-25 -50 -77 -131l64 -28
+q12 -6 74.5 -32t67.5 -28q4 -1 10.5 -25.5t4.5 -30.5zM449 944q3 -15 -4 -28q-12 -23 -50 -38q-30 -12 -60 -12q-26 3 -49 26q-14 15 -18 41l1 3q3 -3 19.5 -5t26.5 0t58 16q36 12 55 14q17 0 21 -17zM1147 815l63 -227l-139 42zM39 15l694 232v1032l-694 -233v-1031z
+M1280 332l102 -31l-181 657l-100 31l-216 -536l102 -31l45 110l211 -65zM777 1294l573 -184v380zM1088 -29l158 -13l-54 -160l-40 66q-130 -83 -276 -108q-58 -12 -91 -12h-84q-79 0 -199.5 39t-183.5 85q-8 7 -8 16q0 8 5 13.5t13 5.5q4 0 18 -7.5t30.5 -16.5t20.5 -11
+q73 -37 159.5 -61.5t157.5 -24.5q95 0 167 14.5t157 50.5q15 7 30.5 15.5t34 19t28.5 16.5zM1536 1050v-1079l-774 246q-14 -6 -375 -127.5t-368 -121.5q-13 0 -18 13q0 1 -1 3v1078q3 9 4 10q5 6 20 11q107 36 149 50v384l558 -198q2 0 160.5 55t316 108.5t161.5 53.5
+q20 0 20 -21v-418z" />
+    <glyph glyph-name="_402" unicode="&#xf1ac;" horiz-adv-x="1792" 
+d="M288 1152q66 0 113 -47t47 -113v-1088q0 -66 -47 -113t-113 -47h-128q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h128zM1664 989q58 -34 93 -93t35 -128v-768q0 -106 -75 -181t-181 -75h-864q-66 0 -113 47t-47 113v1536q0 40 28 68t68 28h672q40 0 88 -20t76 -48
+l152 -152q28 -28 48 -76t20 -88v-163zM928 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 512v128q0 14 -9 23
+t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128
+q14 0 23 9t9 23zM1184 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 256v128q0 14 -9 23t-23 9h-128
+q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1536 896v256h-160q-40 0 -68 28t-28 68v160h-640v-512h896z" />
+    <glyph glyph-name="_403" unicode="&#xf1ad;" 
+d="M1344 1536q26 0 45 -19t19 -45v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280zM512 1248v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 992v-64q0 -14 9 -23t23 -9h64q14 0 23 9
+t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 736v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 480v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 160v64
+q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64
+q14 0 23 9t9 23zM384 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 -96v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9
+t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM896 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 928v64
+q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 160v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64
+q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9
+t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23z" />
+    <glyph glyph-name="_404" unicode="&#xf1ae;" horiz-adv-x="1280" 
+d="M1188 988l-292 -292v-824q0 -46 -33 -79t-79 -33t-79 33t-33 79v384h-64v-384q0 -46 -33 -79t-79 -33t-79 33t-33 79v824l-292 292q-28 28 -28 68t28 68q29 28 68.5 28t67.5 -28l228 -228h368l228 228q28 28 68 28t68 -28q28 -29 28 -68.5t-28 -67.5zM864 1152
+q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
+    <glyph glyph-name="uniF1B1" unicode="&#xf1b0;" horiz-adv-x="1664" 
+d="M780 1064q0 -60 -19 -113.5t-63 -92.5t-105 -39q-76 0 -138 57.5t-92 135.5t-30 151q0 60 19 113.5t63 92.5t105 39q77 0 138.5 -57.5t91.5 -135t30 -151.5zM438 581q0 -80 -42 -139t-119 -59q-76 0 -141.5 55.5t-100.5 133.5t-35 152q0 80 42 139.5t119 59.5
+q76 0 141.5 -55.5t100.5 -134t35 -152.5zM832 608q118 0 255 -97.5t229 -237t92 -254.5q0 -46 -17 -76.5t-48.5 -45t-64.5 -20t-76 -5.5q-68 0 -187.5 45t-182.5 45q-66 0 -192.5 -44.5t-200.5 -44.5q-183 0 -183 146q0 86 56 191.5t139.5 192.5t187.5 146t193 59zM1071 819
+q-61 0 -105 39t-63 92.5t-19 113.5q0 74 30 151.5t91.5 135t138.5 57.5q61 0 105 -39t63 -92.5t19 -113.5q0 -73 -30 -151t-92 -135.5t-138 -57.5zM1503 923q77 0 119 -59.5t42 -139.5q0 -74 -35 -152t-100.5 -133.5t-141.5 -55.5q-77 0 -119 59t-42 139q0 74 35 152.5
+t100.5 134t141.5 55.5z" />
+    <glyph glyph-name="_406" unicode="&#xf1b1;" horiz-adv-x="768" 
+d="M704 1008q0 -145 -57 -243.5t-152 -135.5l45 -821q2 -26 -16 -45t-44 -19h-192q-26 0 -44 19t-16 45l45 821q-95 37 -152 135.5t-57 243.5q0 128 42.5 249.5t117.5 200t160 78.5t160 -78.5t117.5 -200t42.5 -249.5z" />
+    <glyph glyph-name="_407" unicode="&#xf1b2;" horiz-adv-x="1792" 
+d="M896 -93l640 349v636l-640 -233v-752zM832 772l698 254l-698 254l-698 -254zM1664 1024v-768q0 -35 -18 -65t-49 -47l-704 -384q-28 -16 -61 -16t-61 16l-704 384q-31 17 -49 47t-18 65v768q0 40 23 73t61 47l704 256q22 8 44 8t44 -8l704 -256q38 -14 61 -47t23 -73z
+" />
+    <glyph glyph-name="_408" unicode="&#xf1b3;" horiz-adv-x="2304" 
+d="M640 -96l384 192v314l-384 -164v-342zM576 358l404 173l-404 173l-404 -173zM1664 -96l384 192v314l-384 -164v-342zM1600 358l404 173l-404 173l-404 -173zM1152 651l384 165v266l-384 -164v-267zM1088 1030l441 189l-441 189l-441 -189zM2176 512v-416q0 -36 -19 -67
+t-52 -47l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-4 2 -7 4q-2 -2 -7 -4l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-33 16 -52 47t-19 67v416q0 38 21.5 70t56.5 48l434 186v400q0 38 21.5 70t56.5 48l448 192q23 10 50 10t50 -10l448 -192q35 -16 56.5 -48t21.5 -70
+v-400l434 -186q36 -16 57 -48t21 -70z" />
+    <glyph glyph-name="_409" unicode="&#xf1b4;" horiz-adv-x="2048" 
+d="M1848 1197h-511v-124h511v124zM1596 771q-90 0 -146 -52.5t-62 -142.5h408q-18 195 -200 195zM1612 186q63 0 122 32t76 87h221q-100 -307 -427 -307q-214 0 -340.5 132t-126.5 347q0 208 130.5 345.5t336.5 137.5q138 0 240.5 -68t153 -179t50.5 -248q0 -17 -2 -47h-658
+q0 -111 57.5 -171.5t166.5 -60.5zM277 236h296q205 0 205 167q0 180 -199 180h-302v-347zM277 773h281q78 0 123.5 36.5t45.5 113.5q0 144 -190 144h-260v-294zM0 1282h594q87 0 155 -14t126.5 -47.5t90 -96.5t31.5 -154q0 -181 -172 -263q114 -32 172 -115t58 -204
+q0 -75 -24.5 -136.5t-66 -103.5t-98.5 -71t-121 -42t-134 -13h-611v1260z" />
+    <glyph glyph-name="_410" unicode="&#xf1b5;" 
+d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM499 1041h-371v-787h382q117 0 197 57.5t80 170.5q0 158 -143 200q107 52 107 164q0 57 -19.5 96.5
+t-56.5 60.5t-79 29.5t-97 8.5zM477 723h-176v184h163q119 0 119 -90q0 -94 -106 -94zM486 388h-185v217h189q124 0 124 -113q0 -104 -128 -104zM1136 356q-68 0 -104 38t-36 107h411q1 10 1 30q0 132 -74.5 220.5t-203.5 88.5q-128 0 -210 -86t-82 -216q0 -135 79 -217
+t213 -82q205 0 267 191h-138q-11 -34 -47.5 -54t-75.5 -20zM1126 722q113 0 124 -122h-254q4 56 39 89t91 33zM964 988h319v-77h-319v77z" />
+    <glyph glyph-name="_411" unicode="&#xf1b6;" horiz-adv-x="1792" 
+d="M1582 954q0 -101 -71.5 -172.5t-172.5 -71.5t-172.5 71.5t-71.5 172.5t71.5 172.5t172.5 71.5t172.5 -71.5t71.5 -172.5zM812 212q0 104 -73 177t-177 73q-27 0 -54 -6l104 -42q77 -31 109.5 -106.5t1.5 -151.5q-31 -77 -107 -109t-152 -1q-21 8 -62 24.5t-61 24.5
+q32 -60 91 -96.5t130 -36.5q104 0 177 73t73 177zM1642 953q0 126 -89.5 215.5t-215.5 89.5q-127 0 -216.5 -89.5t-89.5 -215.5q0 -127 89.5 -216t216.5 -89q126 0 215.5 89t89.5 216zM1792 953q0 -189 -133.5 -322t-321.5 -133l-437 -319q-12 -129 -109 -218t-229 -89
+q-121 0 -214 76t-118 192l-230 92v429l389 -157q79 48 173 48q13 0 35 -2l284 407q2 187 135.5 319t320.5 132q188 0 321.5 -133.5t133.5 -321.5z" />
+    <glyph glyph-name="_412" unicode="&#xf1b7;" 
+d="M1242 889q0 80 -57 136.5t-137 56.5t-136.5 -57t-56.5 -136q0 -80 56.5 -136.5t136.5 -56.5t137 56.5t57 136.5zM632 301q0 -83 -58 -140.5t-140 -57.5q-56 0 -103 29t-72 77q52 -20 98 -40q60 -24 120 1.5t85 86.5q24 60 -1.5 120t-86.5 84l-82 33q22 5 42 5
+q82 0 140 -57.5t58 -140.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v153l172 -69q20 -92 93.5 -152t168.5 -60q104 0 181 70t87 173l345 252q150 0 255.5 105.5t105.5 254.5q0 150 -105.5 255.5t-255.5 105.5
+q-148 0 -253 -104.5t-107 -252.5l-225 -322q-9 1 -28 1q-75 0 -137 -37l-297 119v468q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5zM1289 887q0 -100 -71 -170.5t-171 -70.5t-170.5 70.5t-70.5 170.5t70.5 171t170.5 71q101 0 171.5 -70.5t70.5 -171.5z
+" />
+    <glyph glyph-name="_413" unicode="&#xf1b8;" horiz-adv-x="1792" 
+d="M836 367l-15 -368l-2 -22l-420 29q-36 3 -67 31.5t-47 65.5q-11 27 -14.5 55t4 65t12 55t21.5 64t19 53q78 -12 509 -28zM449 953l180 -379l-147 92q-63 -72 -111.5 -144.5t-72.5 -125t-39.5 -94.5t-18.5 -63l-4 -21l-190 357q-17 26 -18 56t6 47l8 18q35 63 114 188
+l-140 86zM1680 436l-188 -359q-12 -29 -36.5 -46.5t-43.5 -20.5l-18 -4q-71 -7 -219 -12l8 -164l-230 367l211 362l7 -173q170 -16 283 -5t170 33zM895 1360q-47 -63 -265 -435l-317 187l-19 12l225 356q20 31 60 45t80 10q24 -2 48.5 -12t42 -21t41.5 -33t36 -34.5
+t36 -39.5t32 -35zM1550 1053l212 -363q18 -37 12.5 -76t-27.5 -74q-13 -20 -33 -37t-38 -28t-48.5 -22t-47 -16t-51.5 -14t-46 -12q-34 72 -265 436l313 195zM1407 1279l142 83l-220 -373l-419 20l151 86q-34 89 -75 166t-75.5 123.5t-64.5 80t-47 46.5l-17 13l405 -1
+q31 3 58 -10.5t39 -28.5l11 -15q39 -61 112 -190z" />
+    <glyph glyph-name="_414" unicode="&#xf1b9;" horiz-adv-x="2048" 
+d="M480 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM516 768h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5zM1888 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM2048 544v-384
+q0 -14 -9 -23t-23 -9h-96v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-1024v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5t179 63.5h768q98 0 179 -63.5t104 -157.5
+l105 -419h28q93 0 158.5 -65.5t65.5 -158.5z" />
+    <glyph glyph-name="_415" unicode="&#xf1ba;" horiz-adv-x="2048" 
+d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5
+t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z
+M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
+    <glyph glyph-name="_416" unicode="&#xf1bb;" 
+d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384
+q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
+    <glyph glyph-name="_417" unicode="&#xf1bc;" 
+d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64
+q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37
+q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_418" unicode="&#xf1bd;" horiz-adv-x="1024" 
+d="M1024 1233l-303 -582l24 -31h279v-415h-507l-44 -30l-142 -273l-30 -30h-301v303l303 583l-24 30h-279v415h507l44 30l142 273l30 30h301v-303z" />
+    <glyph glyph-name="_419" unicode="&#xf1be;" horiz-adv-x="2304" 
+d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11
+q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245
+q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785
+l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242
+q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236
+q0 -11 -8 -19t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786
+q-13 2 -22 11t-9 22v899q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
+    <glyph glyph-name="uniF1C0" unicode="&#xf1c0;" 
+d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127
+t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5
+t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
+    <glyph glyph-name="uniF1C1" unicode="&#xf1c1;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197
+q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8
+q-1 1 -1 2q-1 2 -1 3q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
+    <glyph glyph-name="_422" unicode="&#xf1c2;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M233 768v-107h70l164 -661h159l128 485q7 20 10 46q2 16 2 24h4l3 -24q1 -3 3.5 -20t5.5 -26l128 -485h159l164 661h70v107h-300v-107h90l-99 -438q-5 -20 -7 -46l-2 -21h-4q0 3 -0.5 6.5t-1.5 8t-1 6.5q-1 5 -4 21t-5 25l-144 545h-114l-144 -545q-2 -9 -4.5 -24.5
+t-3.5 -21.5l-4 -21h-4l-2 21q-2 26 -7 46l-99 438h90v107h-300z" />
+    <glyph glyph-name="_423" unicode="&#xf1c3;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M429 106v-106h281v106h-75l103 161q5 7 10 16.5t7.5 13.5t3.5 4h2q1 -4 5 -10q2 -4 4.5 -7.5t6 -8t6.5 -8.5l107 -161h-76v-106h291v106h-68l-192 273l195 282h67v107h-279v-107h74l-103 -159q-4 -7 -10 -16.5t-9 -13.5l-2 -3h-2q-1 4 -5 10q-6 11 -17 23l-106 159h76v107
+h-290v-107h68l189 -272l-194 -283h-68z" />
+    <glyph glyph-name="_424" unicode="&#xf1c4;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M416 106v-106h327v106h-93v167h137q76 0 118 15q67 23 106.5 87t39.5 146q0 81 -37 141t-100 87q-48 19 -130 19h-368v-107h92v-555h-92zM769 386h-119v268h120q52 0 83 -18q56 -33 56 -115q0 -89 -62 -120q-31 -15 -78 -15z" />
+    <glyph glyph-name="_425" unicode="&#xf1c5;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M1280 320v-320h-1024v192l192 192l128 -128l384 384zM448 512q-80 0 -136 56t-56 136t56 136t136 56t136 -56t56 -136t-56 -136t-136 -56z" />
+    <glyph glyph-name="_426" unicode="&#xf1c6;" 
+d="M640 1152v128h-128v-128h128zM768 1024v128h-128v-128h128zM640 896v128h-128v-128h128zM768 768v128h-128v-128h128zM1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400
+v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-128v-128h-128v128h-512v-1536h1280zM781 593l107 -349q8 -27 8 -52q0 -83 -72.5 -137.5t-183.5 -54.5t-183.5 54.5t-72.5 137.5q0 25 8 52q21 63 120 396v128h128v-128h79
+q22 0 39 -13t23 -34zM640 128q53 0 90.5 19t37.5 45t-37.5 45t-90.5 19t-90.5 -19t-37.5 -45t37.5 -45t90.5 -19z" />
+    <glyph glyph-name="_427" unicode="&#xf1c7;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M620 686q20 -8 20 -30v-544q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-166 167h-131q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h131l166 167q16 15 35 7zM1037 -3q31 0 50 24q129 159 129 363t-129 363q-16 21 -43 24t-47 -14q-21 -17 -23.5 -43.5t14.5 -47.5
+q100 -123 100 -282t-100 -282q-17 -21 -14.5 -47.5t23.5 -42.5q18 -15 40 -15zM826 145q27 0 47 20q87 93 87 219t-87 219q-18 19 -45 20t-46 -17t-20 -44.5t18 -46.5q52 -57 52 -131t-52 -131q-19 -20 -18 -46.5t20 -44.5q20 -17 44 -17z" />
+    <glyph glyph-name="_428" unicode="&#xf1c8;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M768 768q52 0 90 -38t38 -90v-384q0 -52 -38 -90t-90 -38h-384q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h384zM1260 766q20 -8 20 -30v-576q0 -22 -20 -30q-8 -2 -12 -2q-14 0 -23 9l-265 266v90l265 266q9 9 23 9q4 0 12 -2z" />
+    <glyph glyph-name="_429" unicode="&#xf1c9;" 
+d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z
+M480 768q8 11 21 12.5t24 -6.5l51 -38q11 -8 12.5 -21t-6.5 -24l-182 -243l182 -243q8 -11 6.5 -24t-12.5 -21l-51 -38q-11 -8 -24 -6.5t-21 12.5l-226 301q-14 19 0 38zM1282 467q14 -19 0 -38l-226 -301q-8 -11 -21 -12.5t-24 6.5l-51 38q-11 8 -12.5 21t6.5 24l182 243
+l-182 243q-8 11 -6.5 24t12.5 21l51 38q11 8 24 6.5t21 -12.5zM662 6q-13 2 -20.5 13t-5.5 24l138 831q2 13 13 20.5t24 5.5l63 -10q13 -2 20.5 -13t5.5 -24l-138 -831q-2 -13 -13 -20.5t-24 -5.5z" />
+    <glyph glyph-name="_430" unicode="&#xf1ca;" 
+d="M1497 709v-198q-101 -23 -198 -23q-65 -136 -165.5 -271t-181.5 -215.5t-128 -106.5q-80 -45 -162 3q-28 17 -60.5 43.5t-85 83.5t-102.5 128.5t-107.5 184t-105.5 244t-91.5 314.5t-70.5 390h283q26 -218 70 -398.5t104.5 -317t121.5 -235.5t140 -195q169 169 287 406
+q-142 72 -223 220t-81 333q0 192 104 314.5t284 122.5q178 0 273 -105.5t95 -297.5q0 -159 -58 -286q-7 -1 -19.5 -3t-46 -2t-63 6t-62 25.5t-50.5 51.5q31 103 31 184q0 87 -29 132t-79 45q-53 0 -85 -49.5t-32 -140.5q0 -186 105 -293.5t267 -107.5q62 0 121 14z" />
+    <glyph glyph-name="_431" unicode="&#xf1cb;" horiz-adv-x="1792" 
+d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546
+q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
+    <glyph glyph-name="_432" unicode="&#xf1cc;" horiz-adv-x="2048" 
+d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94
+q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55
+t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97l93 -108q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5t-85 -189.5z" />
+    <glyph glyph-name="_433" unicode="&#xf1cd;" horiz-adv-x="1792" 
+d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194
+q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5
+t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
+    <glyph glyph-name="_434" unicode="&#xf1ce;" horiz-adv-x="1792" 
+d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5
+t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" />
+    <glyph glyph-name="uniF1D0" unicode="&#xf1d0;" horiz-adv-x="1792" 
+d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41
+t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170
+t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136
+q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
+    <glyph glyph-name="uniF1D1" unicode="&#xf1d1;" horiz-adv-x="1792" 
+d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251
+l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162
+q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33
+q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5
+t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71
+t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="uniF1D2" unicode="&#xf1d2;" 
+d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85
+q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392
+q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072
+q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_438" unicode="&#xf1d3;" horiz-adv-x="1792" 
+d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58
+q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47
+q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171
+v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
+    <glyph glyph-name="_439" unicode="&#xf1d4;" 
+d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="uniF1D5" unicode="&#xf1d5;" horiz-adv-x="1280" 
+d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5
+t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153
+t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
+    <glyph glyph-name="uniF1D6" unicode="&#xf1d6;" horiz-adv-x="1792" 
+d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5
+q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20
+t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5
+t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
+    <glyph glyph-name="uniF1D7" unicode="&#xf1d7;" horiz-adv-x="2048" 
+d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25
+q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5
+q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109
+q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
+    <glyph glyph-name="_443" unicode="&#xf1d8;" horiz-adv-x="1792" 
+d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-453 185l-242 -295q-18 -23 -49 -23q-13 0 -22 4q-19 7 -30.5 23.5t-11.5 36.5v349l864 1059l-1069 -925l-395 162q-37 14 -40 55q-2 40 32 59l1664 960q15 9 32 9q20 0 36 -11z" />
+    <glyph glyph-name="_444" unicode="&#xf1d9;" horiz-adv-x="1792" 
+d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-527 215l-298 -327q-18 -21 -47 -21q-14 0 -23 4q-19 7 -30 23.5t-11 36.5v452l-472 193q-37 14 -40 55q-3 39 32 59l1664 960q35 21 68 -2zM1422 26l221 1323l-1434 -827l336 -137
+l863 639l-478 -797z" />
+    <glyph glyph-name="_445" unicode="&#xf1da;" 
+d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5
+t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298zM896 928v-448q0 -14 -9 -23
+t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="_446" unicode="&#xf1db;" 
+d="M768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103
+t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_447" unicode="&#xf1dc;" horiz-adv-x="1792" 
+d="M1682 -128q-44 0 -132.5 3.5t-133.5 3.5q-44 0 -132 -3.5t-132 -3.5q-24 0 -37 20.5t-13 45.5q0 31 17 46t39 17t51 7t45 15q33 21 33 140l-1 391q0 21 -1 31q-13 4 -50 4h-675q-38 0 -51 -4q-1 -10 -1 -31l-1 -371q0 -142 37 -164q16 -10 48 -13t57 -3.5t45 -15
+t20 -45.5q0 -26 -12.5 -48t-36.5 -22q-47 0 -139.5 3.5t-138.5 3.5q-43 0 -128 -3.5t-127 -3.5q-23 0 -35.5 21t-12.5 45q0 30 15.5 45t36 17.5t47.5 7.5t42 15q33 23 33 143l-1 57v813q0 3 0.5 26t0 36.5t-1.5 38.5t-3.5 42t-6.5 36.5t-11 31.5t-16 18q-15 10 -45 12t-53 2
+t-41 14t-18 45q0 26 12 48t36 22q46 0 138.5 -3.5t138.5 -3.5q42 0 126.5 3.5t126.5 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17 -43.5t-38.5 -14.5t-49.5 -4t-43 -13q-35 -21 -35 -160l1 -320q0 -21 1 -32q13 -3 39 -3h699q25 0 38 3q1 11 1 32l1 320q0 139 -35 160
+q-18 11 -58.5 12.5t-66 13t-25.5 49.5q0 26 12.5 48t37.5 22q44 0 132 -3.5t132 -3.5q43 0 129 3.5t129 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17.5 -44t-40 -14.5t-51.5 -3t-44 -12.5q-35 -23 -35 -161l1 -943q0 -119 34 -140q16 -10 46 -13.5t53.5 -4.5t41.5 -15.5t18 -44.5
+q0 -26 -12 -48t-36 -22z" />
+    <glyph glyph-name="_448" unicode="&#xf1dd;" horiz-adv-x="1280" 
+d="M1278 1347v-73q0 -29 -18.5 -61t-42.5 -32q-50 0 -54 -1q-26 -6 -32 -31q-3 -11 -3 -64v-1152q0 -25 -18 -43t-43 -18h-108q-25 0 -43 18t-18 43v1218h-143v-1218q0 -25 -17.5 -43t-43.5 -18h-108q-26 0 -43.5 18t-17.5 43v496q-147 12 -245 59q-126 58 -192 179
+q-64 117 -64 259q0 166 88 286q88 118 209 159q111 37 417 37h479q25 0 43 -18t18 -43z" />
+    <glyph glyph-name="_449" unicode="&#xf1de;" 
+d="M352 128v-128h-352v128h352zM704 256q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM864 640v-128h-864v128h864zM224 1152v-128h-224v128h224zM1536 128v-128h-736v128h736zM576 1280q26 0 45 -19t19 -45v-256
+q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1216 768q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1536 640v-128h-224v128h224zM1536 1152v-128h-864v128h864z" />
+    <glyph glyph-name="uniF1E0" unicode="&#xf1e0;" 
+d="M1216 512q133 0 226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5t-226.5 93.5t-93.5 226.5q0 12 2 34l-360 180q-92 -86 -218 -86q-133 0 -226.5 93.5t-93.5 226.5t93.5 226.5t226.5 93.5q126 0 218 -86l360 180q-2 22 -2 34q0 133 93.5 226.5t226.5 93.5
+t226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5q-126 0 -218 86l-360 -180q2 -22 2 -34t-2 -34l360 -180q92 86 218 86z" />
+    <glyph glyph-name="_451" unicode="&#xf1e1;" 
+d="M1280 341q0 88 -62.5 151t-150.5 63q-84 0 -145 -58l-241 120q2 16 2 23t-2 23l241 120q61 -58 145 -58q88 0 150.5 63t62.5 151t-62.5 150.5t-150.5 62.5t-151 -62.5t-63 -150.5q0 -7 2 -23l-241 -120q-62 57 -145 57q-88 0 -150.5 -62.5t-62.5 -150.5t62.5 -150.5
+t150.5 -62.5q83 0 145 57l241 -120q-2 -16 -2 -23q0 -88 63 -150.5t151 -62.5t150.5 62.5t62.5 150.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_452" unicode="&#xf1e2;" horiz-adv-x="1792" 
+d="M571 947q-10 25 -34 35t-49 0q-108 -44 -191 -127t-127 -191q-10 -25 0 -49t35 -34q13 -5 24 -5q42 0 60 40q34 84 98.5 148.5t148.5 98.5q25 11 35 35t0 49zM1513 1303l46 -46l-244 -243l68 -68q19 -19 19 -45.5t-19 -45.5l-64 -64q89 -161 89 -343q0 -143 -55.5 -273.5
+t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5q182 0 343 -89l64 64q19 19 45.5 19t45.5 -19l68 -68zM1521 1359q-10 -10 -22 -10q-13 0 -23 10l-91 90q-9 10 -9 23t9 23q10 9 23 9t23 -9l90 -91
+q10 -9 10 -22.5t-10 -22.5zM1751 1129q-11 -9 -23 -9t-23 9l-90 91q-10 9 -10 22.5t10 22.5q9 10 22.5 10t22.5 -10l91 -90q9 -10 9 -23t-9 -23zM1792 1312q0 -14 -9 -23t-23 -9h-96q-14 0 -23 9t-9 23t9 23t23 9h96q14 0 23 -9t9 -23zM1600 1504v-96q0 -14 -9 -23t-23 -9
+t-23 9t-9 23v96q0 14 9 23t23 9t23 -9t9 -23zM1751 1449l-91 -90q-10 -10 -22 -10q-13 0 -23 10q-10 9 -10 22.5t10 22.5l90 91q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
+    <glyph glyph-name="_453" unicode="&#xf1e3;" horiz-adv-x="1792" 
+d="M609 720l287 208l287 -208l-109 -336h-355zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM1515 186q149 203 149 454v3l-102 -89l-240 224l63 323
+l134 -12q-150 206 -389 282l53 -124l-287 -159l-287 159l53 124q-239 -76 -389 -282l135 12l62 -323l-240 -224l-102 89v-3q0 -251 149 -454l30 132l326 -40l139 -298l-116 -69q117 -39 240 -39t240 39l-116 69l139 298l326 40z" />
+    <glyph glyph-name="_454" unicode="&#xf1e4;" horiz-adv-x="1792" 
+d="M448 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM256 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM832 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23
+v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM66 768q-28 0 -47 19t-19 46v129h514v-129q0 -27 -19 -46t-46 -19h-383zM1216 224v-192q0 -14 -9 -23t-23 -9h-192
+q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1600 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23
+zM1408 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1016v-13h-514v10q0 104 -382 102q-382 -1 -382 -102v-10h-514v13q0 17 8.5 43t34 64t65.5 75.5t110.5 76t160 67.5t224 47.5t293.5 18.5t293 -18.5t224 -47.5
+t160.5 -67.5t110.5 -76t65.5 -75.5t34 -64t8.5 -43zM1792 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 962v-129q0 -27 -19 -46t-46 -19h-384q-27 0 -46 19t-19 46v129h514z" />
+    <glyph glyph-name="_455" unicode="&#xf1e5;" horiz-adv-x="1792" 
+d="M704 1216v-768q0 -26 -19 -45t-45 -19v-576q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v512l249 873q7 23 31 23h424zM1024 1216v-704h-256v704h256zM1792 320v-512q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v576q-26 0 -45 19t-19 45v768h424q24 0 31 -23z
+M736 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23zM1408 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="_456" unicode="&#xf1e6;" horiz-adv-x="1792" 
+d="M1755 1083q37 -38 37 -90.5t-37 -90.5l-401 -400l150 -150l-160 -160q-163 -163 -389.5 -186.5t-411.5 100.5l-362 -362h-181v181l362 362q-124 185 -100.5 411.5t186.5 389.5l160 160l150 -150l400 401q38 37 91 37t90 -37t37 -90.5t-37 -90.5l-400 -401l234 -234
+l401 400q38 37 91 37t90 -37z" />
+    <glyph glyph-name="_457" unicode="&#xf1e7;" horiz-adv-x="1792" 
+d="M873 796q0 -83 -63.5 -142.5t-152.5 -59.5t-152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59t152.5 -59t63.5 -143zM1375 796q0 -83 -63 -142.5t-153 -59.5q-89 0 -152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59q90 0 153 -59t63 -143zM1600 616v667q0 87 -32 123.5
+t-111 36.5h-1112q-83 0 -112.5 -34t-29.5 -126v-673q43 -23 88.5 -40t81 -28t81 -18.5t71 -11t70 -4t58.5 -0.5t56.5 2t44.5 2q68 1 95 -27q6 -6 10 -9q26 -25 61 -51q7 91 118 87q5 0 36.5 -1.5t43 -2t45.5 -1t53 1t54.5 4.5t61 8.5t62 13.5t67 19.5t67.5 27t72 34.5z
+M1763 621q-121 -149 -372 -252q84 -285 -23 -465q-66 -113 -183 -148q-104 -32 -182 15q-86 51 -82 164l-1 326v1q-8 2 -24.5 6t-23.5 5l-1 -338q4 -114 -83 -164q-79 -47 -183 -15q-117 36 -182 150q-105 180 -22 463q-251 103 -372 252q-25 37 -4 63t60 -1q4 -2 11.5 -7
+t10.5 -8v694q0 72 47 123t114 51h1257q67 0 114 -51t47 -123v-694l21 15q39 27 60 1t-4 -63z" />
+    <glyph glyph-name="_458" unicode="&#xf1e8;" horiz-adv-x="1792" 
+d="M896 1102v-434h-145v434h145zM1294 1102v-434h-145v434h145zM1294 342l253 254v795h-1194v-1049h326v-217l217 217h398zM1692 1536v-1013l-434 -434h-326l-217 -217h-217v217h-398v1158l109 289h1483z" />
+    <glyph glyph-name="_459" unicode="&#xf1e9;" 
+d="M773 217v-127q-1 -292 -6 -305q-12 -32 -51 -40q-54 -9 -181.5 38t-162.5 89q-13 15 -17 36q-1 12 4 26q4 10 34 47t181 216q1 0 60 70q15 19 39.5 24.5t49.5 -3.5q24 -10 37.5 -29t12.5 -42zM624 468q-3 -55 -52 -70l-120 -39q-275 -88 -292 -88q-35 2 -54 36
+q-12 25 -17 75q-8 76 1 166.5t30 124.5t56 32q13 0 202 -77q71 -29 115 -47l84 -34q23 -9 35.5 -30.5t11.5 -48.5zM1450 171q-7 -54 -91.5 -161t-135.5 -127q-37 -14 -63 7q-14 10 -184 287l-47 77q-14 21 -11.5 46t19.5 46q35 43 83 26q1 -1 119 -40q203 -66 242 -79.5
+t47 -20.5q28 -22 22 -61zM778 803q5 -102 -54 -122q-58 -17 -114 71l-378 598q-8 35 19 62q41 43 207.5 89.5t224.5 31.5q40 -10 49 -45q3 -18 22 -305.5t24 -379.5zM1440 695q3 -39 -26 -59q-15 -10 -329 -86q-67 -15 -91 -23l1 2q-23 -6 -46 4t-37 32q-30 47 0 87
+q1 1 75 102q125 171 150 204t34 39q28 19 65 2q48 -23 123 -133.5t81 -167.5v-3z" />
+    <glyph glyph-name="_460" unicode="&#xf1ea;" horiz-adv-x="2048" 
+d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19
+t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
+    <glyph glyph-name="_461" unicode="&#xf1eb;" horiz-adv-x="2048" 
+d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121
+q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z
+M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
+    <glyph glyph-name="_462" unicode="&#xf1ec;" horiz-adv-x="1792" 
+d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5
+t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5
+t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5
+t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z
+M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38
+h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_463" unicode="&#xf1ed;" 
+d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246
+q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598
+q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
+    <glyph glyph-name="_464" unicode="&#xf1ee;" horiz-adv-x="1792" 
+d="M441 864q33 0 52 -26q266 -364 362 -774h-446q-127 441 -367 749q-12 16 -3 33.5t29 17.5h373zM1000 507q-49 -199 -125 -393q-79 310 -256 594q40 221 44 449q211 -340 337 -650zM1099 1216q235 -324 384.5 -698.5t184.5 -773.5h-451q-41 665 -553 1472h435zM1792 640
+q0 -424 -101 -812q-67 560 -359 1083q-25 301 -106 584q-4 16 5.5 28.5t25.5 12.5h359q21 0 38.5 -13t22.5 -33q115 -409 115 -850z" />
+    <glyph glyph-name="uniF1F0" unicode="&#xf1f0;" horiz-adv-x="2304" 
+d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27
+q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128
+q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_466" unicode="&#xf1f1;" horiz-adv-x="2304" 
+d="M1119 1195q-128 85 -281 85q-103 0 -197.5 -40.5t-162.5 -108.5t-108.5 -162t-40.5 -197q0 -104 40.5 -198t108.5 -162t162 -108.5t198 -40.5q153 0 281 85q-131 107 -178 265.5t0.5 316.5t177.5 265zM1152 1171q-126 -99 -172 -249.5t-0.5 -300.5t172.5 -249
+q127 99 172.5 249t-0.5 300.5t-172 249.5zM1185 1195q130 -107 177.5 -265.5t0.5 -317t-178 -264.5q128 -85 281 -85q104 0 198 40.5t162 108.5t108.5 162t40.5 198q0 103 -40.5 197t-108.5 162t-162.5 108.5t-197.5 40.5q-153 0 -281 -85zM1926 473h7v3h-17v-3h7v-17h3v17z
+M1955 456h4v20h-5l-6 -13l-6 13h-5v-20h3v15l6 -13h4l5 13v-15zM1947 16v-2h-2h-3v3h3h2v-1zM1947 7h3l-4 5h2l1 1q1 1 1 3t-1 3l-1 1h-3h-6v-13h3v5h1zM685 75q0 19 11 31t30 12q18 0 29 -12.5t11 -30.5q0 -19 -11 -31t-29 -12q-19 0 -30 12t-11 31zM1158 119q30 0 35 -32
+h-70q5 32 35 32zM1514 75q0 19 11 31t29 12t29.5 -12.5t11.5 -30.5q0 -19 -11 -31t-30 -12q-18 0 -29 12t-11 31zM1786 75q0 18 11.5 30.5t29.5 12.5t29.5 -12.5t11.5 -30.5q0 -19 -11.5 -31t-29.5 -12t-29.5 12.5t-11.5 30.5zM1944 3q-2 0 -4 1q-1 0 -3 2t-2 3q-1 2 -1 4
+q0 3 1 4q0 2 2 4l1 1q2 0 2 1q2 1 4 1q3 0 4 -1l4 -2l2 -4v-1q1 -2 1 -3l-1 -1v-3t-1 -1l-1 -2q-2 -2 -4 -2q-1 -1 -4 -1zM599 7h30v85q0 24 -14.5 38.5t-39.5 15.5q-32 0 -47 -24q-14 24 -45 24q-24 0 -39 -20v16h-30v-135h30v75q0 36 33 36q30 0 30 -36v-75h29v75
+q0 36 33 36q30 0 30 -36v-75zM765 7h29v68v67h-29v-16q-17 20 -43 20q-29 0 -48 -20t-19 -51t19 -51t48 -20q28 0 43 20v-17zM943 48q0 34 -47 40l-14 2q-23 4 -23 14q0 15 25 15q23 0 43 -11l12 24q-22 14 -55 14q-26 0 -41 -12t-15 -32q0 -33 47 -39l13 -2q24 -4 24 -14
+q0 -17 -31 -17q-25 0 -45 14l-13 -23q25 -17 58 -17q29 0 45.5 12t16.5 32zM1073 14l-8 25q-13 -7 -26 -7q-19 0 -19 22v61h48v27h-48v41h-30v-41h-28v-27h28v-61q0 -50 47 -50q21 0 36 10zM1159 146q-29 0 -48 -20t-19 -51q0 -32 19.5 -51.5t49.5 -19.5q33 0 55 19l-14 22
+q-18 -15 -39 -15q-34 0 -41 33h101v12q0 32 -18 51.5t-46 19.5zM1318 146q-23 0 -35 -20v16h-30v-135h30v76q0 35 29 35q10 0 18 -4l9 28q-9 4 -21 4zM1348 75q0 -31 19.5 -51t52.5 -20q29 0 48 16l-14 24q-18 -13 -35 -12q-18 0 -29.5 12t-11.5 31t11.5 31t29.5 12
+q19 0 35 -12l14 24q-20 16 -48 16q-33 0 -52.5 -20t-19.5 -51zM1593 7h30v68v67h-30v-16q-15 20 -42 20q-29 0 -48.5 -20t-19.5 -51t19.5 -51t48.5 -20q28 0 42 20v-17zM1726 146q-23 0 -35 -20v16h-29v-135h29v76q0 35 29 35q10 0 18 -4l9 28q-8 4 -21 4zM1866 7h29v68v122
+h-29v-71q-15 20 -43 20t-47.5 -20.5t-19.5 -50.5t19.5 -50.5t47.5 -20.5q29 0 43 20v-17zM1944 27l-2 -1h-3q-2 -1 -4 -3q-3 -1 -3 -4q-1 -2 -1 -6q0 -3 1 -5q0 -2 3 -4q2 -2 4 -3t5 -1q4 0 6 1q0 1 2 2l2 1q1 1 3 4q1 2 1 5q0 4 -1 6q-1 1 -3 4q0 1 -2 2l-2 1q-1 0 -3 0.5
+t-3 0.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_467" unicode="&#xf1f2;" horiz-adv-x="2304" 
+d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42
+q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604
+v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569
+q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73
+t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
+    <glyph glyph-name="f1f3" unicode="&#xf1f3;" horiz-adv-x="2304" 
+d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z
+M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260
+l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279
+v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040
+q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168
+q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5
+t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21
+h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5
+t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
+    <glyph glyph-name="_469" unicode="&#xf1f4;" horiz-adv-x="2304" 
+d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16
+t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76
+q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59
+t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489
+l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66
+q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_470" unicode="&#xf1f5;" horiz-adv-x="2304" 
+d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109
+q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118
+q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151
+q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31
+q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_471" unicode="&#xf1f6;" horiz-adv-x="2048" 
+d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5
+l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5
+l418 363q10 8 23.5 7t21.5 -11z" />
+    <glyph glyph-name="_472" unicode="&#xf1f7;" horiz-adv-x="2048" 
+d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128
+q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161
+q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
+    <glyph glyph-name="_473" unicode="&#xf1f8;" horiz-adv-x="1408" 
+d="M512 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM768 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1024 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704
+q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167
+q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="_474" unicode="&#xf1f9;" 
+d="M1150 462v-109q0 -50 -36.5 -89t-94 -60.5t-118 -32.5t-117.5 -11q-205 0 -342.5 139t-137.5 346q0 203 136 339t339 136q34 0 75.5 -4.5t93 -18t92.5 -34t69 -56.5t28 -81v-109q0 -16 -16 -16h-118q-16 0 -16 16v70q0 43 -65.5 67.5t-137.5 24.5q-140 0 -228.5 -91.5
+t-88.5 -237.5q0 -151 91.5 -249.5t233.5 -98.5q68 0 138 24t70 66v70q0 7 4.5 11.5t10.5 4.5h119q6 0 11 -4.5t5 -11.5zM768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5
+t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_475" unicode="&#xf1fa;" 
+d="M972 761q0 108 -53.5 169t-147.5 61q-63 0 -124 -30.5t-110 -84.5t-79.5 -137t-30.5 -180q0 -112 53.5 -173t150.5 -61q96 0 176 66.5t122.5 166t42.5 203.5zM1536 640q0 -111 -37 -197t-98.5 -135t-131.5 -74.5t-145 -27.5q-6 0 -15.5 -0.5t-16.5 -0.5q-95 0 -142 53
+q-28 33 -33 83q-52 -66 -131.5 -110t-173.5 -44q-161 0 -249.5 95.5t-88.5 269.5q0 157 66 290t179 210.5t246 77.5q87 0 155 -35.5t106 -99.5l2 19l11 56q1 6 5.5 12t9.5 6h118q5 0 13 -11q5 -5 3 -16l-120 -614q-5 -24 -5 -48q0 -39 12.5 -52t44.5 -13q28 1 57 5.5t73 24
+t77 50t57 89.5t24 137q0 292 -174 466t-466 174q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51q228 0 405 144q11 9 24 8t21 -12l41 -49q8 -12 7 -24q-2 -13 -12 -22q-102 -83 -227.5 -128t-258.5 -45q-156 0 -298 61
+t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q344 0 556 -212t212 -556z" />
+    <glyph glyph-name="_476" unicode="&#xf1fb;" horiz-adv-x="1792" 
+d="M1698 1442q94 -94 94 -226.5t-94 -225.5l-225 -223l104 -104q10 -10 10 -23t-10 -23l-210 -210q-10 -10 -23 -10t-23 10l-105 105l-603 -603q-37 -37 -90 -37h-203l-256 -128l-64 64l128 256v203q0 53 37 90l603 603l-105 105q-10 10 -10 23t10 23l210 210q10 10 23 10
+t23 -10l104 -104l223 225q93 94 225.5 94t226.5 -94zM512 64l576 576l-192 192l-576 -576v-192h192z" />
+    <glyph glyph-name="f1fc" unicode="&#xf1fc;" horiz-adv-x="1792" 
+d="M1615 1536q70 0 122.5 -46.5t52.5 -116.5q0 -63 -45 -151q-332 -629 -465 -752q-97 -91 -218 -91q-126 0 -216.5 92.5t-90.5 219.5q0 128 92 212l638 579q59 54 130 54zM706 502q39 -76 106.5 -130t150.5 -76l1 -71q4 -213 -129.5 -347t-348.5 -134q-123 0 -218 46.5
+t-152.5 127.5t-86.5 183t-29 220q7 -5 41 -30t62 -44.5t59 -36.5t46 -17q41 0 55 37q25 66 57.5 112.5t69.5 76t88 47.5t103 25.5t125 10.5z" />
+    <glyph glyph-name="_478" unicode="&#xf1fd;" horiz-adv-x="1792" 
+d="M1792 128v-384h-1792v384q45 0 85 14t59 27.5t47 37.5q30 27 51.5 38t56.5 11q24 0 44 -7t31 -15t33 -27q29 -25 47 -38t58 -27t86 -14q45 0 85 14.5t58 27t48 37.5q21 19 32.5 27t31 15t43.5 7q35 0 56.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14t85 14t59 27.5
+t47 37.5q30 27 51.5 38t56.5 11q34 0 55.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14zM1792 448v-192q-24 0 -44 7t-31 15t-33 27q-29 25 -47 38t-58 27t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-22 -19 -33 -27t-31 -15t-44 -7q-35 0 -56.5 11t-51.5 38q-29 25 -47 38
+t-58 27t-86 14q-45 0 -85 -14.5t-58 -27t-48 -37.5q-21 -19 -32.5 -27t-31 -15t-43.5 -7q-35 0 -56.5 11t-51.5 38q-28 24 -47 37.5t-59 27.5t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-30 -27 -51.5 -38t-56.5 -11v192q0 80 56 136t136 56h64v448h256v-448h256v448h256v-448
+h256v448h256v-448h64q80 0 136 -56t56 -136zM512 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1024 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5
+q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1536 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150z" />
+    <glyph glyph-name="_479" unicode="&#xf1fe;" horiz-adv-x="2048" 
+d="M2048 0v-128h-2048v1536h128v-1408h1920zM1664 1024l256 -896h-1664v576l448 576l576 -576z" />
+    <glyph glyph-name="_480" unicode="&#xf200;" horiz-adv-x="1792" 
+d="M768 646l546 -546q-106 -108 -247.5 -168t-298.5 -60q-209 0 -385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103v-762zM955 640h773q0 -157 -60 -298.5t-168 -247.5zM1664 768h-768v768q209 0 385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_481" unicode="&#xf201;" horiz-adv-x="2048" 
+d="M2048 0v-128h-2048v1536h128v-1408h1920zM1920 1248v-435q0 -21 -19.5 -29.5t-35.5 7.5l-121 121l-633 -633q-10 -10 -23 -10t-23 10l-233 233l-416 -416l-192 192l585 585q10 10 23 10t23 -10l233 -233l464 464l-121 121q-16 16 -7.5 35.5t29.5 19.5h435q14 0 23 -9
+t9 -23z" />
+    <glyph glyph-name="_482" unicode="&#xf202;" horiz-adv-x="1792" 
+d="M1292 832q0 -6 10 -41q10 -29 25 -49.5t41 -34t44 -20t55 -16.5q325 -91 325 -332q0 -146 -105.5 -242.5t-254.5 -96.5q-59 0 -111.5 18.5t-91.5 45.5t-77 74.5t-63 87.5t-53.5 103.5t-43.5 103t-39.5 106.5t-35.5 95q-32 81 -61.5 133.5t-73.5 96.5t-104 64t-142 20
+q-96 0 -183 -55.5t-138 -144.5t-51 -185q0 -160 106.5 -279.5t263.5 -119.5q177 0 258 95q56 63 83 116l84 -152q-15 -34 -44 -70l1 -1q-131 -152 -388 -152q-147 0 -269.5 79t-190.5 207.5t-68 274.5q0 105 43.5 206t116 176.5t172 121.5t204.5 46q87 0 159 -19t123.5 -50
+t95 -80t72.5 -99t58.5 -117t50.5 -124.5t50 -130.5t55 -127q96 -200 233 -200q81 0 138.5 48.5t57.5 128.5q0 42 -19 72t-50.5 46t-72.5 31.5t-84.5 27t-87.5 34t-81 52t-65 82t-39 122.5q-3 16 -3 33q0 110 87.5 192t198.5 78q78 -3 120.5 -14.5t90.5 -53.5h-1
+q12 -11 23 -24.5t26 -36t19 -27.5l-129 -99q-26 49 -54 70v1q-23 21 -97 21q-49 0 -84 -33t-35 -83z" />
+    <glyph glyph-name="_483" unicode="&#xf203;" 
+d="M1432 484q0 173 -234 239q-35 10 -53 16.5t-38 25t-29 46.5q0 2 -2 8.5t-3 12t-1 7.5q0 36 24.5 59.5t60.5 23.5q54 0 71 -15h-1q20 -15 39 -51l93 71q-39 54 -49 64q-33 29 -67.5 39t-85.5 10q-80 0 -142 -57.5t-62 -137.5q0 -7 2 -23q16 -96 64.5 -140t148.5 -73
+q29 -8 49 -15.5t45 -21.5t38.5 -34.5t13.5 -46.5v-5q1 -58 -40.5 -93t-100.5 -35q-97 0 -167 144q-23 47 -51.5 121.5t-48 125.5t-54 110.5t-74 95.5t-103.5 60.5t-147 24.5q-101 0 -192 -56t-144 -148t-50 -192v-1q4 -108 50.5 -199t133.5 -147.5t196 -56.5q186 0 279 110
+q20 27 31 51l-60 109q-42 -80 -99 -116t-146 -36q-115 0 -191 87t-76 204q0 105 82 189t186 84q112 0 170 -53.5t104 -172.5q8 -21 25.5 -68.5t28.5 -76.5t31.5 -74.5t38.5 -74t45.5 -62.5t55.5 -53.5t66 -33t80 -13.5q107 0 183 69.5t76 174.5zM1536 1120v-960
+q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_484" unicode="&#xf204;" horiz-adv-x="2048" 
+d="M1152 640q0 104 -40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM1920 640q0 104 -40.5 198.5
+t-109.5 163.5t-163.5 109.5t-198.5 40.5h-386q119 -90 188.5 -224t69.5 -288t-69.5 -288t-188.5 -224h386q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM2048 640q0 -130 -51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5
+t-136.5 204t-51 248.5t51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5z" />
+    <glyph glyph-name="_485" unicode="&#xf205;" horiz-adv-x="2048" 
+d="M0 640q0 130 51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5t-51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5t-136.5 204t-51 248.5zM1408 128q104 0 198.5 40.5t163.5 109.5
+t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" />
+    <glyph glyph-name="_486" unicode="&#xf206;" horiz-adv-x="2304" 
+d="M762 384h-314q-40 0 -57.5 35t6.5 67l188 251q-65 31 -137 31q-132 0 -226 -94t-94 -226t94 -226t226 -94q115 0 203 72.5t111 183.5zM576 512h186q-18 85 -75 148zM1056 512l288 384h-480l-99 -132q105 -103 126 -252h165zM2176 448q0 132 -94 226t-226 94
+q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94t226 94t94 226zM2304 448q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 97 39.5 183.5t109.5 149.5l-65 98l-353 -469
+q-18 -26 -51 -26h-197q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q114 0 215 -55l137 183h-224q-26 0 -45 19t-19 45t19 45t45 19h384v-128h435l-85 128h-222q-26 0 -45 19t-19 45t19 45t45 19h256q33 0 53 -28l267 -400
+q91 44 192 44q185 0 316.5 -131.5t131.5 -316.5z" />
+    <glyph glyph-name="_487" unicode="&#xf207;" 
+d="M384 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1362 716l-72 384q-5 23 -22.5 37.5t-40.5 14.5
+h-918q-23 0 -40.5 -14.5t-22.5 -37.5l-72 -384q-5 -30 14 -53t49 -23h1062q30 0 49 23t14 53zM1136 1328q0 20 -14 34t-34 14h-640q-20 0 -34 -14t-14 -34t14 -34t34 -14h640q20 0 34 14t14 34zM1536 603v-603h-128v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5
+t-37.5 90.5v128h-768v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5v128h-128v603q0 112 25 223l103 454q9 78 97.5 137t230 89t312.5 30t312.5 -30t230 -89t97.5 -137l105 -454q23 -102 23 -223z" />
+    <glyph glyph-name="_488" unicode="&#xf208;" horiz-adv-x="2048" 
+d="M1463 704q0 -35 -25 -60.5t-61 -25.5h-702q-36 0 -61 25.5t-25 60.5t25 60.5t61 25.5h702q36 0 61 -25.5t25 -60.5zM1677 704q0 86 -23 170h-982q-36 0 -61 25t-25 60q0 36 25 61t61 25h908q-88 143 -235 227t-320 84q-177 0 -327.5 -87.5t-238 -237.5t-87.5 -327
+q0 -86 23 -170h982q36 0 61 -25t25 -60q0 -36 -25 -61t-61 -25h-908q88 -143 235.5 -227t320.5 -84q132 0 253 51.5t208 139t139 208t52 253.5zM2048 959q0 -35 -25 -60t-61 -25h-131q17 -85 17 -170q0 -167 -65.5 -319.5t-175.5 -263t-262.5 -176t-319.5 -65.5
+q-246 0 -448.5 133t-301.5 350h-189q-36 0 -61 25t-25 61q0 35 25 60t61 25h132q-17 85 -17 170q0 167 65.5 319.5t175.5 263t262.5 176t320.5 65.5q245 0 447.5 -133t301.5 -350h188q36 0 61 -25t25 -61z" />
+    <glyph glyph-name="_489" unicode="&#xf209;" horiz-adv-x="1280" 
+d="M953 1158l-114 -328l117 -21q165 451 165 518q0 56 -38 56q-57 0 -130 -225zM654 471l33 -88q37 42 71 67l-33 5.5t-38.5 7t-32.5 8.5zM362 1367q0 -98 159 -521q17 10 49 10q15 0 75 -5l-121 351q-75 220 -123 220q-19 0 -29 -17.5t-10 -37.5zM283 608q0 -36 51.5 -119
+t117.5 -153t100 -70q14 0 25.5 13t11.5 27q0 24 -32 102q-13 32 -32 72t-47.5 89t-61.5 81t-62 32q-20 0 -45.5 -27t-25.5 -47zM125 273q0 -41 25 -104q59 -145 183.5 -227t281.5 -82q227 0 382 170q152 169 152 427q0 43 -1 67t-11.5 62t-30.5 56q-56 49 -211.5 75.5
+t-270.5 26.5q-37 0 -49 -11q-12 -5 -12 -35q0 -34 21.5 -60t55.5 -40t77.5 -23.5t87.5 -11.5t85 -4t70 0h23q24 0 40 -19q15 -19 19 -55q-28 -28 -96 -54q-61 -22 -93 -46q-64 -46 -108.5 -114t-44.5 -137q0 -31 18.5 -88.5t18.5 -87.5l-3 -12q-4 -12 -4 -14
+q-137 10 -146 216q-8 -2 -41 -2q2 -7 2 -21q0 -53 -40.5 -89.5t-94.5 -36.5q-82 0 -166.5 78t-84.5 159q0 34 33 67q52 -64 60 -76q77 -104 133 -104q12 0 26.5 8.5t14.5 20.5q0 34 -87.5 145t-116.5 111q-43 0 -70 -44.5t-27 -90.5zM11 264q0 101 42.5 163t136.5 88
+q-28 74 -28 104q0 62 61 123t122 61q29 0 70 -15q-163 462 -163 567q0 80 41 130.5t119 50.5q131 0 325 -581q6 -17 8 -23q6 16 29 79.5t43.5 118.5t54 127.5t64.5 123t70.5 86.5t76.5 36q71 0 112 -49t41 -122q0 -108 -159 -550q61 -15 100.5 -46t58.5 -78t26 -93.5
+t7 -110.5q0 -150 -47 -280t-132 -225t-211 -150t-278 -55q-111 0 -223 42q-149 57 -258 191.5t-109 286.5z" />
+    <glyph glyph-name="_490" unicode="&#xf20a;" horiz-adv-x="2048" 
+d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206
+q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307
+t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14
+t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
+    <glyph glyph-name="_491" unicode="&#xf20b;" 
+d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5
+t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="_492" unicode="&#xf20c;" 
+d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55
+q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410
+q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
+    <glyph glyph-name="_493" unicode="&#xf20d;" 
+d="M915 450h-294l147 551zM1001 128h311l-324 1024h-440l-324 -1024h311l383 314zM1536 1120v-960q0 -118 -85 -203t-203 -85h-960q-118 0 -203 85t-85 203v960q0 118 85 203t203 85h960q118 0 203 -85t85 -203z" />
+    <glyph glyph-name="_494" unicode="&#xf20e;" horiz-adv-x="2048" 
+d="M2048 641q0 -21 -13 -36.5t-33 -19.5l-205 -356q3 -9 3 -18q0 -20 -12.5 -35.5t-32.5 -19.5l-193 -337q3 -8 3 -16q0 -23 -16.5 -40t-40.5 -17q-25 0 -41 18h-400q-17 -20 -43 -20t-43 20h-399q-17 -20 -43 -20q-23 0 -40 16.5t-17 40.5q0 8 4 20l-193 335
+q-20 4 -32.5 19.5t-12.5 35.5q0 9 3 18l-206 356q-20 5 -32.5 20.5t-12.5 35.5q0 21 13.5 36.5t33.5 19.5l199 344q0 1 -0.5 3t-0.5 3q0 36 34 51l209 363q-4 10 -4 18q0 24 17 40.5t40 16.5q26 0 44 -21h396q16 21 43 21t43 -21h398q18 21 44 21q23 0 40 -16.5t17 -40.5
+q0 -6 -4 -18l207 -358q23 -1 39 -17.5t16 -38.5q0 -13 -7 -27l187 -324q19 -4 31.5 -19.5t12.5 -35.5zM1063 -158h389l-342 354h-143l-342 -354h360q18 16 39 16t39 -16zM112 654q1 -4 1 -13q0 -10 -2 -15l208 -360l15 -6l188 199v347l-187 194q-13 -8 -29 -10zM986 1438
+h-388l190 -200l554 200h-280q-16 -16 -38 -16t-38 16zM1689 226q1 6 5 11l-64 68l-17 -79h76zM1583 226l22 105l-252 266l-296 -307l63 -64h463zM1495 -142l16 28l65 310h-427l333 -343q8 4 13 5zM578 -158h5l342 354h-373v-335l4 -6q14 -5 22 -13zM552 226h402l64 66
+l-309 321l-157 -166v-221zM359 226h163v189l-168 -177q4 -8 5 -12zM358 1051q0 -1 0.5 -2t0.5 -2q0 -16 -8 -29l171 -177v269zM552 1121v-311l153 -157l297 314l-223 236zM556 1425l-4 -8v-264l205 74l-191 201q-6 -2 -10 -3zM1447 1438h-16l-621 -224l213 -225zM1023 946
+l-297 -315l311 -319l296 307zM688 634l-136 141v-284zM1038 270l-42 -44h85zM1374 618l238 -251l132 624l-3 5l-1 1zM1718 1018q-8 13 -8 29v2l-216 376q-5 1 -13 5l-437 -463l310 -327zM522 1142v223l-163 -282zM522 196h-163l163 -283v283zM1607 196l-48 -227l130 227h-82
+zM1729 266l207 361q-2 10 -2 14q0 1 3 16l-171 296l-129 -612l77 -82q5 3 15 7z" />
+    <glyph glyph-name="f210" unicode="&#xf210;" 
+d="M0 856q0 131 91.5 226.5t222.5 95.5h742l352 358v-1470q0 -132 -91.5 -227t-222.5 -95h-780q-131 0 -222.5 95t-91.5 227v790zM1232 102l-176 180v425q0 46 -32 79t-78 33h-484q-46 0 -78 -33t-32 -79v-492q0 -46 32.5 -79.5t77.5 -33.5h770z" />
+    <glyph glyph-name="_496" unicode="&#xf211;" 
+d="M934 1386q-317 -121 -556 -362.5t-358 -560.5q-20 89 -20 176q0 208 102.5 384.5t278.5 279t384 102.5q82 0 169 -19zM1203 1267q93 -65 164 -155q-389 -113 -674.5 -400.5t-396.5 -676.5q-93 72 -155 162q112 386 395 671t667 399zM470 -67q115 356 379.5 622t619.5 384
+q40 -92 54 -195q-292 -120 -516 -345t-343 -518q-103 14 -194 52zM1536 -125q-193 50 -367 115q-135 -84 -290 -107q109 205 274 370.5t369 275.5q-21 -152 -101 -284q65 -175 115 -370z" />
+    <glyph glyph-name="f212" unicode="&#xf212;" horiz-adv-x="2048" 
+d="M1893 1144l155 -1272q-131 0 -257 57q-200 91 -393 91q-226 0 -374 -148q-148 148 -374 148q-193 0 -393 -91q-128 -57 -252 -57h-5l155 1272q224 127 482 127q233 0 387 -106q154 106 387 106q258 0 482 -127zM1398 157q129 0 232 -28.5t260 -93.5l-124 1021
+q-171 78 -368 78q-224 0 -374 -141q-150 141 -374 141q-197 0 -368 -78l-124 -1021q105 43 165.5 65t148.5 39.5t178 17.5q202 0 374 -108q172 108 374 108zM1438 191l-55 907q-211 -4 -359 -155q-152 155 -374 155q-176 0 -336 -66l-114 -941q124 51 228.5 76t221.5 25
+q209 0 374 -102q172 107 374 102z" />
+    <glyph glyph-name="_498" unicode="&#xf213;" horiz-adv-x="2048" 
+d="M1500 165v733q0 21 -15 36t-35 15h-93q-20 0 -35 -15t-15 -36v-733q0 -20 15 -35t35 -15h93q20 0 35 15t15 35zM1216 165v531q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-531q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM924 165v429q0 20 -15 35t-35 15h-101
+q-20 0 -35 -15t-15 -35v-429q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM632 165v362q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-362q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM2048 311q0 -166 -118 -284t-284 -118h-1244q-166 0 -284 118t-118 284
+q0 116 63 214.5t168 148.5q-10 34 -10 73q0 113 80.5 193.5t193.5 80.5q102 0 180 -67q45 183 194 300t338 117q149 0 275 -73.5t199.5 -199.5t73.5 -275q0 -66 -14 -122q135 -33 221 -142.5t86 -247.5z" />
+    <glyph glyph-name="_499" unicode="&#xf214;" 
+d="M0 1536h1536v-1392l-776 -338l-760 338v1392zM1436 209v926h-1336v-926l661 -294zM1436 1235v201h-1336v-201h1336zM181 937v-115h-37v115h37zM181 789v-115h-37v115h37zM181 641v-115h-37v115h37zM181 493v-115h-37v115h37zM181 345v-115h-37v115h37zM207 202l15 34
+l105 -47l-15 -33zM343 142l15 34l105 -46l-15 -34zM478 82l15 34l105 -46l-15 -34zM614 23l15 33l104 -46l-15 -34zM797 10l105 46l15 -33l-105 -47zM932 70l105 46l15 -34l-105 -46zM1068 130l105 46l15 -34l-105 -46zM1203 189l105 47l15 -34l-105 -46zM259 1389v-36h-114
+v36h114zM421 1389v-36h-115v36h115zM583 1389v-36h-115v36h115zM744 1389v-36h-114v36h114zM906 1389v-36h-114v36h114zM1068 1389v-36h-115v36h115zM1230 1389v-36h-115v36h115zM1391 1389v-36h-114v36h114zM181 1049v-79h-37v115h115v-36h-78zM421 1085v-36h-115v36h115z
+M583 1085v-36h-115v36h115zM744 1085v-36h-114v36h114zM906 1085v-36h-114v36h114zM1068 1085v-36h-115v36h115zM1230 1085v-36h-115v36h115zM1355 970v79h-78v36h115v-115h-37zM1355 822v115h37v-115h-37zM1355 674v115h37v-115h-37zM1355 526v115h37v-115h-37zM1355 378
+v115h37v-115h-37zM1355 230v115h37v-115h-37zM760 265q-129 0 -221 91.5t-92 221.5q0 129 92 221t221 92q130 0 221.5 -92t91.5 -221q0 -130 -91.5 -221.5t-221.5 -91.5zM595 646q0 -36 19.5 -56.5t49.5 -25t64 -7t64 -2t49.5 -9t19.5 -30.5q0 -49 -112 -49q-97 0 -123 51
+h-3l-31 -63q67 -42 162 -42q29 0 56.5 5t55.5 16t45.5 33t17.5 53q0 46 -27.5 69.5t-67.5 27t-79.5 3t-67 5t-27.5 25.5q0 21 20.5 33t40.5 15t41 3q34 0 70.5 -11t51.5 -34h3l30 58q-3 1 -21 8.5t-22.5 9t-19.5 7t-22 7t-20 4.5t-24 4t-23 1q-29 0 -56.5 -5t-54 -16.5
+t-43 -34t-16.5 -53.5z" />
+    <glyph glyph-name="_500" unicode="&#xf215;" horiz-adv-x="2048" 
+d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832
+q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" />
+    <glyph glyph-name="_501" unicode="&#xf216;" horiz-adv-x="2048" 
+d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126t-103.5 132.5t-108.5 126.5t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5
+t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113
+t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5
+q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" />
+    <glyph glyph-name="_502" unicode="&#xf217;" horiz-adv-x="1664" 
+d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5
+t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920
+q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="_503" unicode="&#xf218;" horiz-adv-x="1664" 
+d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5
+t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920
+q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="_504" unicode="&#xf219;" horiz-adv-x="2048" 
+d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20
+l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" />
+    <glyph glyph-name="_505" unicode="&#xf21a;" horiz-adv-x="2048" 
+d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83
+q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83
+q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314
+v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83
+q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" />
+    <glyph glyph-name="_506" unicode="&#xf21b;" 
+d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14
+t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5
+q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31
+t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" />
+    <glyph glyph-name="_507" unicode="&#xf21c;" horiz-adv-x="2304" 
+d="M2301 500q12 -103 -22 -198.5t-99 -163.5t-158.5 -106t-196.5 -31q-161 11 -279.5 125t-134.5 274q-12 111 27.5 210.5t118.5 170.5l-71 107q-96 -80 -151 -194t-55 -244q0 -27 -18.5 -46.5t-45.5 -19.5h-256h-69q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5
+t-131.5 316.5t131.5 316.5t316.5 131.5q76 0 152 -27l24 45q-123 110 -304 110h-64q-26 0 -45 19t-19 45t19 45t45 19h128q78 0 145 -13.5t116.5 -38.5t71.5 -39.5t51 -36.5h512h115l-85 128h-222q-30 0 -49 22.5t-14 52.5q4 23 23 38t43 15h253q33 0 53 -28l70 -105
+l114 114q19 19 46 19h101q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-179l115 -172q131 63 275 36q143 -26 244 -134.5t118 -253.5zM448 128q115 0 203 72.5t111 183.5h-314q-35 0 -55 31q-18 32 -1 63l147 277q-47 13 -91 13q-132 0 -226 -94t-94 -226t94 -226
+t226 -94zM1856 128q132 0 226 94t94 226t-94 226t-226 94q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94z" />
+    <glyph glyph-name="_508" unicode="&#xf21d;" 
+d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12
+q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384
+q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5
+t158.5 -65.5t65.5 -158.5z" />
+    <glyph glyph-name="_509" unicode="&#xf21e;" horiz-adv-x="1792" 
+d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221
+q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124
+t127 -344z" />
+    <glyph glyph-name="venus" unicode="&#xf221;" horiz-adv-x="1280" 
+d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292
+q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" />
+    <glyph glyph-name="_511" unicode="&#xf222;" 
+d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5
+q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_512" unicode="&#xf223;" horiz-adv-x="1280" 
+d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5
+t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5
+t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_513" unicode="&#xf224;" 
+d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64
+q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5
+t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_514" unicode="&#xf225;" horiz-adv-x="1792" 
+d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64
+q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9
+t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5
+t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_515" unicode="&#xf226;" horiz-adv-x="1792" 
+d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23
+t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391
+q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391
+q0 -226 -154 -391q103 -57 218 -57z" />
+    <glyph glyph-name="_516" unicode="&#xf227;" horiz-adv-x="1920" 
+d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230
+q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9
+t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128
+q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -28 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" />
+    <glyph glyph-name="_517" unicode="&#xf228;" horiz-adv-x="2048" 
+d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23
+t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9
+t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5
+t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" />
+    <glyph glyph-name="_518" unicode="&#xf229;" 
+d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5
+t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5
+t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_519" unicode="&#xf22a;" horiz-adv-x="1280" 
+d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22
+t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5
+t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_520" unicode="&#xf22b;" horiz-adv-x="2048" 
+d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5
+t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5
+t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_521" unicode="&#xf22c;" horiz-adv-x="1280" 
+d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5
+t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
+    <glyph glyph-name="_522" unicode="&#xf22d;" horiz-adv-x="1280" 
+d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123
+t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" />
+    <glyph glyph-name="_523" unicode="&#xf22e;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="_524" unicode="&#xf22f;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="_525" unicode="&#xf230;" 
+d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" />
+    <glyph glyph-name="_526" unicode="&#xf231;" horiz-adv-x="1280" 
+d="M0 939q0 108 37.5 203.5t103.5 166.5t152 123t185 78t202 26q158 0 294 -66.5t221 -193.5t85 -287q0 -96 -19 -188t-60 -177t-100 -149.5t-145 -103t-189 -38.5q-68 0 -135 32t-96 88q-10 -39 -28 -112.5t-23.5 -95t-20.5 -71t-26 -71t-32 -62.5t-46 -77.5t-62 -86.5
+l-14 -5l-9 10q-15 157 -15 188q0 92 21.5 206.5t66.5 287.5t52 203q-32 65 -32 169q0 83 52 156t132 73q61 0 95 -40.5t34 -102.5q0 -66 -44 -191t-44 -187q0 -63 45 -104.5t109 -41.5q55 0 102 25t78.5 68t56 95t38 110.5t20 111t6.5 99.5q0 173 -109.5 269.5t-285.5 96.5
+q-200 0 -334 -129.5t-134 -328.5q0 -44 12.5 -85t27 -65t27 -45.5t12.5 -30.5q0 -28 -15 -73t-37 -45q-2 0 -17 3q-51 15 -90.5 56t-61 94.5t-32.5 108t-11 106.5z" />
+    <glyph glyph-name="_527" unicode="&#xf232;" 
+d="M985 562q13 0 97.5 -44t89.5 -53q2 -5 2 -15q0 -33 -17 -76q-16 -39 -71 -65.5t-102 -26.5q-57 0 -190 62q-98 45 -170 118t-148 185q-72 107 -71 194v8q3 91 74 158q24 22 52 22q6 0 18 -1.5t19 -1.5q19 0 26.5 -6.5t15.5 -27.5q8 -20 33 -88t25 -75q0 -21 -34.5 -57.5
+t-34.5 -46.5q0 -7 5 -15q34 -73 102 -137q56 -53 151 -101q12 -7 22 -7q15 0 54 48.5t52 48.5zM782 32q127 0 243.5 50t200.5 134t134 200.5t50 243.5t-50 243.5t-134 200.5t-200.5 134t-243.5 50t-243.5 -50t-200.5 -134t-134 -200.5t-50 -243.5q0 -203 120 -368l-79 -233
+l242 77q158 -104 345 -104zM782 1414q153 0 292.5 -60t240.5 -161t161 -240.5t60 -292.5t-60 -292.5t-161 -240.5t-240.5 -161t-292.5 -60q-195 0 -365 94l-417 -134l136 405q-108 178 -108 389q0 153 60 292.5t161 240.5t240.5 161t292.5 60z" />
+    <glyph glyph-name="_528" unicode="&#xf233;" horiz-adv-x="1792" 
+d="M128 128h1024v128h-1024v-128zM128 640h1024v128h-1024v-128zM1696 192q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM128 1152h1024v128h-1024v-128zM1696 704q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1696 1216
+q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1792 384v-384h-1792v384h1792zM1792 896v-384h-1792v384h1792zM1792 1408v-384h-1792v384h1792z" />
+    <glyph glyph-name="_529" unicode="&#xf234;" horiz-adv-x="2048" 
+d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1664 512h352q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-352q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5
+t-9.5 22.5v352h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5v-352zM928 288q0 -52 38 -90t90 -38h256v-238q-68 -50 -171 -50h-874q-121 0 -194 69t-73 190q0 53 3.5 103.5t14 109t26.5 108.5
+t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q79 -61 154.5 -91.5t164.5 -30.5t164.5 30.5t154.5 91.5q20 17 39 17q132 0 217 -96h-223q-52 0 -90 -38t-38 -90v-192z" />
+    <glyph glyph-name="_530" unicode="&#xf235;" horiz-adv-x="2048" 
+d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1781 320l249 -249q9 -9 9 -23q0 -13 -9 -22l-136 -136q-9 -9 -22 -9q-14 0 -23 9l-249 249l-249 -249q-9 -9 -23 -9q-13 0 -22 9l-136 136
+q-9 9 -9 22q0 14 9 23l249 249l-249 249q-9 9 -9 23q0 13 9 22l136 136q9 9 22 9q14 0 23 -9l249 -249l249 249q9 9 23 9q13 0 22 -9l136 -136q9 -9 9 -22q0 -14 -9 -23zM1283 320l-181 -181q-37 -37 -37 -91q0 -53 37 -90l83 -83q-21 -3 -44 -3h-874q-121 0 -194 69
+t-73 190q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q154 -122 319 -122t319 122q20 17 39 17q28 0 57 -6q-28 -27 -41 -50t-13 -56q0 -54 37 -91z" />
+    <glyph glyph-name="_531" unicode="&#xf236;" horiz-adv-x="2048" 
+d="M256 512h1728q26 0 45 -19t19 -45v-448h-256v256h-1536v-256h-256v1216q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-704zM832 832q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM2048 576v64q0 159 -112.5 271.5t-271.5 112.5h-704
+q-26 0 -45 -19t-19 -45v-384h1152z" />
+    <glyph glyph-name="_532" unicode="&#xf237;" 
+d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" />
+    <glyph glyph-name="_533" unicode="&#xf238;" 
+d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56
+t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
+    <glyph glyph-name="_534" unicode="&#xf239;" 
+d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47
+t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
+    <glyph glyph-name="_535" unicode="&#xf23a;" horiz-adv-x="1792" 
+d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116
+q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" />
+    <glyph glyph-name="_536" unicode="&#xf23b;" 
+d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
+    <glyph glyph-name="_537" unicode="&#xf23c;" horiz-adv-x="2296" 
+d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5
+q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5
+q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42
+q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37
+q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5
+q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139
+q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 5 5 8q16 18 60 23h13q5 18 19 30t33 8
+t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132
+q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132
+q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z
+M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-105 2 -211 0v1q-1 -27 2.5 -86
+t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103
+q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34q0 2 0.5 3.5t1.5 3t1 2.5v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4
+l-10 -2.5t-12 -2l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-6 -1 -9 -1q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130
+t-73 70q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -1 -1 -4t-1 -5q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150
+q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12
+q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
+    <glyph glyph-name="_538" unicode="&#xf23d;" horiz-adv-x="2304" 
+d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5
+t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5
+t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
+    <glyph glyph-name="_539" unicode="&#xf23e;" horiz-adv-x="1792" 
+d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348
+t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23
+t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96
+q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512
+q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" />
+    <glyph glyph-name="_540" unicode="&#xf240;" horiz-adv-x="2304" 
+d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113
+v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" />
+    <glyph glyph-name="_541" unicode="&#xf241;" horiz-adv-x="2304" 
+d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9
+h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+    <glyph glyph-name="_542" unicode="&#xf242;" horiz-adv-x="2304" 
+d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9
+h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+    <glyph glyph-name="_543" unicode="&#xf243;" horiz-adv-x="2304" 
+d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9
+h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+    <glyph glyph-name="_544" unicode="&#xf244;" horiz-adv-x="2304" 
+d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23
+v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
+    <glyph glyph-name="_545" unicode="&#xf245;" horiz-adv-x="1280" 
+d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" />
+    <glyph glyph-name="_546" unicode="&#xf246;" horiz-adv-x="1024" 
+d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" />
+    <glyph glyph-name="_547" unicode="&#xf247;" horiz-adv-x="2048" 
+d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128
+h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" />
+    <glyph glyph-name="_548" unicode="&#xf248;" horiz-adv-x="2304" 
+d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256
+v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" />
+    <glyph glyph-name="_549" unicode="&#xf249;" 
+d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" />
+    <glyph glyph-name="_550" unicode="&#xf24a;" 
+d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68
+z" />
+    <glyph glyph-name="_551" unicode="&#xf24b;" horiz-adv-x="2304" 
+d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5
+t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88
+t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90
+t90 38h2048q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_552" unicode="&#xf24c;" horiz-adv-x="2304" 
+d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294
+t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z
+M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_553" unicode="&#xf24d;" horiz-adv-x="1792" 
+d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113
+zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="_554" unicode="&#xf24e;" horiz-adv-x="2304" 
+d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64
+q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91
+t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5
+t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" />
+    <glyph glyph-name="_555" unicode="&#xf250;" 
+d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9
+t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5
+t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+    <glyph glyph-name="_556" unicode="&#xf251;" 
+d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9
+t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" />
+    <glyph glyph-name="_557" unicode="&#xf252;" 
+d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9
+t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" />
+    <glyph glyph-name="_558" unicode="&#xf253;" 
+d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9
+t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196
+h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
+    <glyph glyph-name="_559" unicode="&#xf254;" 
+d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87
+t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9
+h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" />
+    <glyph glyph-name="_560" unicode="&#xf255;" 
+d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25
+q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27
+t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21
+q72 69 174 69z" />
+    <glyph glyph-name="_561" unicode="&#xf256;" horiz-adv-x="1792" 
+d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33
+t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52
+h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" />
+    <glyph glyph-name="_562" unicode="&#xf257;" horiz-adv-x="1792" 
+d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668
+q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17
+t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5
+t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5
+q0 -42 -23 -78t-61 -53l-310 -141h91z" />
+    <glyph glyph-name="_563" unicode="&#xf258;" horiz-adv-x="2048" 
+d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32
+q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68
+q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" />
+    <glyph glyph-name="_564" unicode="&#xf259;" horiz-adv-x="2048" 
+d="M459 -256q-77 0 -137.5 47.5t-79.5 122.5l-101 401q-13 57 -13 108q0 45 -5 67l-116 477q-7 27 -7 57q0 93 62 161t155 78q17 85 82.5 139t152.5 54q83 0 148 -51.5t85 -132.5l83 -348l103 428q20 81 85 132.5t148 51.5q89 0 155.5 -57.5t80.5 -144.5q92 -10 152 -79
+t60 -162q0 -24 -7 -59l-123 -512q10 7 37.5 28.5t38.5 29.5t35 23t41 20.5t41.5 11t49.5 5.5q105 0 180 -74t75 -179q0 -62 -28.5 -118t-78.5 -94l-507 -380q-68 -51 -153 -51h-694zM1104 1408q-38 0 -68.5 -24t-39.5 -62l-164 -682h-127l-145 602q-9 38 -39.5 62t-68.5 24
+q-48 0 -80 -33t-32 -80q0 -15 3 -28l132 -547h-26l-99 408q-9 37 -40 62.5t-69 25.5q-47 0 -80 -33t-33 -79q0 -14 3 -26l116 -478q7 -28 9 -86t10 -88l100 -401q8 -32 34 -52.5t59 -20.5h694q42 0 76 26l507 379q56 43 56 110q0 52 -37.5 88.5t-89.5 36.5q-43 0 -77 -26
+l-307 -230v227q0 4 32 138t68 282t39 161q4 18 4 29q0 47 -32 81t-79 34q-39 0 -69.5 -24t-39.5 -62l-116 -482h-26l150 624q3 14 3 28q0 48 -31.5 82t-79.5 34z" />
+    <glyph glyph-name="_565" unicode="&#xf25a;" horiz-adv-x="1792" 
+d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5
+q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5
+v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32
+v-384h32z" />
+    <glyph glyph-name="_566" unicode="&#xf25b;" 
+d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181
+v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46
+q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5
+q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308
+q0 -53 37.5 -90.5t90.5 -37.5h668z" />
+    <glyph glyph-name="_567" unicode="&#xf25c;" horiz-adv-x="1973" 
+d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5
+t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141
+q13 0 22 -8.5t10 -20.5z" />
+    <glyph glyph-name="_568" unicode="&#xf25d;" horiz-adv-x="1792" 
+d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109
+t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640
+q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="_569" unicode="&#xf25e;" horiz-adv-x="1792" 
+d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13.5t-23.5 -14.5t-28.5 -13t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78
+q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13.5t-23.5 -14.5t-28.5 -13t-33.5 -9.5
+t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376
+q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191
+t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" />
+    <glyph glyph-name="f260" unicode="&#xf260;" horiz-adv-x="2048" 
+d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" />
+    <glyph glyph-name="f261" unicode="&#xf261;" horiz-adv-x="1792" 
+d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191
+t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="_572" unicode="&#xf262;" horiz-adv-x="2304" 
+d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57
+t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197
+t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5
+t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5
+t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5
+q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" />
+    <glyph glyph-name="f263" unicode="&#xf263;" horiz-adv-x="1280" 
+d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5
+t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94
+q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" />
+    <glyph glyph-name="_574" unicode="&#xf264;" 
+d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32
+q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5
+zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="_575" unicode="&#xf265;" horiz-adv-x="1720" 
+d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33
+l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" />
+    <glyph glyph-name="_576" unicode="&#xf266;" horiz-adv-x="2304" 
+d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540
+q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81
+l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" />
+    <glyph glyph-name="_577" unicode="&#xf267;" horiz-adv-x="1792" 
+d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640
+q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5
+t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5
+t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5
+t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191
+t191 -286t71 -348z" />
+    <glyph glyph-name="_578" unicode="&#xf268;" horiz-adv-x="1792" 
+d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962
+q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
+    <glyph glyph-name="_579" unicode="&#xf269;" horiz-adv-x="1792" 
+d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5
+q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5
+q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
+    <glyph glyph-name="_580" unicode="&#xf26a;" horiz-adv-x="1792" 
+d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339
+q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z
+" />
+    <glyph glyph-name="_581" unicode="&#xf26b;" horiz-adv-x="1792" 
+d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606
+q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z
+M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" />
+    <glyph glyph-name="_582" unicode="&#xf26c;" horiz-adv-x="2048" 
+d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23
+v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="_583" unicode="&#xf26d;" horiz-adv-x="1792" 
+d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34
+h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100
+q-68 175 -180 287z" />
+    <glyph glyph-name="_584" unicode="&#xf26e;" 
+d="M1401 -11l-6 -6q-113 -113 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6
+q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13
+q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 33 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249
+q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 33 -6t30 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183
+q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46
+t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" />
+    <glyph glyph-name="_585" unicode="&#xf270;" horiz-adv-x="1792" 
+d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z
+M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30
+q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57
+t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133
+q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
+    <glyph glyph-name="_586" unicode="&#xf271;" horiz-adv-x="1792" 
+d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9
+h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224
+v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
+    <glyph glyph-name="_587" unicode="&#xf272;" horiz-adv-x="1792" 
+d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23
+t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47
+t47 -113v-96h128q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_588" unicode="&#xf273;" horiz-adv-x="1792" 
+d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z
+M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280
+q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_589" unicode="&#xf274;" horiz-adv-x="1792" 
+d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23
+t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47
+t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
+    <glyph glyph-name="_590" unicode="&#xf275;" horiz-adv-x="1792" 
+d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" />
+    <glyph glyph-name="_591" unicode="&#xf276;" horiz-adv-x="1024" 
+d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q62 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249
+q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" />
+    <glyph glyph-name="_592" unicode="&#xf277;" horiz-adv-x="1792" 
+d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768
+q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" />
+    <glyph glyph-name="_593" unicode="&#xf278;" horiz-adv-x="2048" 
+d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173
+v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" />
+    <glyph glyph-name="_594" unicode="&#xf279;" horiz-adv-x="1792" 
+d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472
+q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" />
+    <glyph glyph-name="_595" unicode="&#xf27a;" horiz-adv-x="1792" 
+d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5
+t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37
+t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
+    <glyph glyph-name="_596" unicode="&#xf27b;" horiz-adv-x="1792" 
+d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5
+t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5
+t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51
+t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" />
+    <glyph glyph-name="_597" unicode="&#xf27c;" horiz-adv-x="1024" 
+d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" />
+    <glyph glyph-name="_598" unicode="&#xf27d;" horiz-adv-x="1792" 
+d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246
+q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
+    <glyph glyph-name="f27e" unicode="&#xf27e;" 
+d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
+    <glyph glyph-name="uniF280" unicode="&#xf280;" 
+d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72
+h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275
+l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
+    <glyph glyph-name="uniF281" unicode="&#xf281;" horiz-adv-x="1792" 
+d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5
+l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44
+t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106
+q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" />
+    <glyph glyph-name="_602" unicode="&#xf282;" horiz-adv-x="1792" 
+d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53
+q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" />
+    <glyph glyph-name="_603" unicode="&#xf283;" horiz-adv-x="2304" 
+d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
+    <glyph glyph-name="_604" unicode="&#xf284;" horiz-adv-x="1792" 
+d="M1584 246l-218 111q-74 -120 -196.5 -189t-263.5 -69q-147 0 -271 72t-196 196t-72 270q0 110 42.5 209.5t115 172t172 115t209.5 42.5q131 0 247.5 -60.5t192.5 -168.5l215 125q-110 169 -286.5 265t-378.5 96q-161 0 -308 -63t-253 -169t-169 -253t-63 -308t63 -308
+t169 -253t253 -169t308 -63q213 0 397.5 107t290.5 292zM1030 643l693 -352q-116 -253 -334.5 -400t-492.5 -147q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q260 0 470.5 -133.5t335.5 -366.5zM1543 640h-39v-160h-96v352h136q32 0 54.5 -20
+t28.5 -48t1 -56t-27.5 -48t-57.5 -20z" />
+    <glyph glyph-name="uniF285" unicode="&#xf285;" horiz-adv-x="1792" 
+d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
+    <glyph glyph-name="uniF286" unicode="&#xf286;" horiz-adv-x="1792" 
+d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96
+q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 6 2.5 9.5t8.5 5t9.5 2t11.5 0t9 -0.5v391q-32 15 -32 50q0 23 16.5 39t38.5 16t38.5 -16t16.5 -39q0 -35 -32 -50v-17q45 10 83 10q21 0 59.5 -7.5t54.5 -7.5
+q17 0 47 7.5t37 7.5q16 0 16 -16v-210q0 -15 -35 -21.5t-62 -6.5q-18 0 -54.5 7.5t-55.5 7.5q-40 0 -90 -12v-133q1 0 9 0.5t11.5 0t9.5 -2t8.5 -5t2.5 -9.5v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96
+q16 0 16 -16z" />
+    <glyph glyph-name="_607" unicode="&#xf287;" horiz-adv-x="2304" 
+d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96
+q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5
+t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
+    <glyph glyph-name="_608" unicode="&#xf288;" horiz-adv-x="1792" 
+d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348
+t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="_609" unicode="&#xf289;" horiz-adv-x="2304" 
+d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22
+q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5
+q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13
+q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
+    <glyph glyph-name="_610" unicode="&#xf28a;" 
+d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83
+t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20
+q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5
+t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" />
+    <glyph glyph-name="_611" unicode="&#xf28b;" 
+d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103
+t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_612" unicode="&#xf28c;" 
+d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273
+t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" />
+    <glyph glyph-name="_613" unicode="&#xf28d;" 
+d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5
+t103 -385.5z" />
+    <glyph glyph-name="_614" unicode="&#xf28e;" 
+d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273
+t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" />
+    <glyph glyph-name="_615" unicode="&#xf290;" horiz-adv-x="1792" 
+d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5
+t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
+    <glyph glyph-name="_616" unicode="&#xf291;" horiz-adv-x="2048" 
+d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5
+t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416
+q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441
+h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" />
+    <glyph glyph-name="_617" unicode="&#xf292;" horiz-adv-x="1792" 
+d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12
+q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311
+q15 0 25 -12q9 -12 6 -28z" />
+    <glyph glyph-name="_618" unicode="&#xf293;" 
+d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5
+t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
+    <glyph glyph-name="_619" unicode="&#xf294;" horiz-adv-x="1024" 
+d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
+    <glyph glyph-name="_620" unicode="&#xf295;" 
+d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5
+t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5
+t271.5 -112.5t112.5 -271.5z" />
+    <glyph glyph-name="_621" unicode="&#xf296;" horiz-adv-x="1792" 
+d="M104 830l792 -1015l-868 630q-18 13 -25 34.5t0 42.5l101 308v0zM566 830h660l-330 -1015v0zM368 1442l198 -612h-462l198 612q8 23 33 23t33 -23zM1688 830l101 -308q7 -21 0 -42.5t-25 -34.5l-868 -630l792 1015v0zM1688 830h-462l198 612q8 23 33 23t33 -23z" />
+    <glyph glyph-name="_622" unicode="&#xf297;" horiz-adv-x="1792" 
+d="M384 704h160v224h-160v-224zM1221 372v92q-104 -36 -243 -38q-135 -1 -259.5 46.5t-220.5 122.5l1 -96q88 -80 212 -128.5t272 -47.5q129 0 238 49zM640 704h640v224h-640v-224zM1792 736q0 -187 -99 -352q89 -102 89 -229q0 -157 -129.5 -268t-313.5 -111
+q-122 0 -225 52.5t-161 140.5q-19 -1 -57 -1t-57 1q-58 -88 -161 -140.5t-225 -52.5q-184 0 -313.5 111t-129.5 268q0 127 89 229q-99 165 -99 352q0 209 120 385.5t326.5 279.5t449.5 103t449.5 -103t326.5 -279.5t120 -385.5z" />
+    <glyph glyph-name="_623" unicode="&#xf298;" 
+d="M515 625v-128h-252v128h252zM515 880v-127h-252v127h252zM1273 369v-128h-341v128h341zM1273 625v-128h-672v128h672zM1273 880v-127h-672v127h672zM1408 20v1240q0 8 -6 14t-14 6h-32l-378 -256l-210 171l-210 -171l-378 256h-32q-8 0 -14 -6t-6 -14v-1240q0 -8 6 -14
+t14 -6h1240q8 0 14 6t6 14zM553 1130l185 150h-406zM983 1130l221 150h-406zM1536 1260v-1240q0 -62 -43 -105t-105 -43h-1240q-62 0 -105 43t-43 105v1240q0 62 43 105t105 43h1240q62 0 105 -43t43 -105z" />
+    <glyph glyph-name="_624" unicode="&#xf299;" horiz-adv-x="1792" 
+d="M896 720q-104 196 -160 278q-139 202 -347 318q-34 19 -70 36q-89 40 -94 32t34 -38l39 -31q62 -43 112.5 -93.5t94.5 -116.5t70.5 -113t70.5 -131q9 -17 13 -25q44 -84 84 -153t98 -154t115.5 -150t131 -123.5t148.5 -90.5q153 -66 154 -60q1 3 -49 37q-53 36 -81 57
+q-77 58 -179 211t-185 310zM549 177q-76 60 -132.5 125t-98 143.5t-71 154.5t-58.5 186t-52 209t-60.5 252t-76.5 289q273 0 497.5 -36t379 -92t271 -144.5t185.5 -172.5t110 -198.5t56 -199.5t12.5 -198.5t-9.5 -173t-20 -143.5t-13 -107l323 -327h-104l-281 285
+q-22 -2 -91.5 -14t-121.5 -19t-138 -6t-160.5 17t-167.5 59t-179 111z" />
+    <glyph glyph-name="_625" unicode="&#xf29a;" horiz-adv-x="1792" 
+d="M1374 879q-6 26 -28.5 39.5t-48.5 7.5q-261 -62 -401 -62t-401 62q-26 6 -48.5 -7.5t-28.5 -39.5t7.5 -48.5t39.5 -28.5q194 -46 303 -58q-2 -158 -15.5 -269t-26.5 -155.5t-41 -115.5l-9 -21q-10 -25 1 -49t36 -34q9 -4 23 -4q44 0 60 41l8 20q54 139 71 259h42
+q17 -120 71 -259l8 -20q16 -41 60 -41q14 0 23 4q25 10 36 34t1 49l-9 21q-28 71 -41 115.5t-26.5 155.5t-15.5 269q109 12 303 58q26 6 39.5 28.5t7.5 48.5zM1024 1024q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z
+M1600 640q0 -143 -55.5 -273.5t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5zM896 1408q-156 0 -298 -61t-245 -164t-164 -245t-61 -298t61 -298
+t164 -245t245 -164t298 -61t298 61t245 164t164 245t61 298t-61 298t-164 245t-245 164t-298 61zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="_626" unicode="&#xf29b;" 
+d="M1438 723q34 -35 29 -82l-44 -551q-4 -42 -34.5 -70t-71.5 -28q-6 0 -9 1q-44 3 -72.5 36.5t-25.5 77.5l35 429l-143 -8q55 -113 55 -240q0 -216 -148 -372l-137 137q91 101 91 235q0 145 -102.5 248t-247.5 103q-134 0 -236 -92l-137 138q120 114 284 141l264 300
+l-149 87l-181 -161q-33 -30 -77 -27.5t-73 35.5t-26.5 77t34.5 73l239 213q26 23 60 26.5t64 -14.5l488 -283q36 -21 48 -68q17 -67 -26 -117l-205 -232l371 20q49 3 83 -32zM1240 1180q-74 0 -126 52t-52 126t52 126t126 52t126.5 -52t52.5 -126t-52.5 -126t-126.5 -52z
+M613 -62q106 0 196 61l139 -139q-146 -116 -335 -116q-148 0 -273.5 73t-198.5 198t-73 273q0 188 116 336l139 -139q-60 -88 -60 -197q0 -145 102.5 -247.5t247.5 -102.5z" />
+    <glyph glyph-name="_627" unicode="&#xf29c;" 
+d="M880 336v-160q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h160q14 0 23 -9t9 -23zM1136 832q0 -50 -15 -90t-45.5 -69t-52 -44t-59.5 -36q-32 -18 -46.5 -28t-26 -24t-11.5 -29v-32q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v68q0 35 10.5 64.5
+t24 47.5t39 35.5t41 25.5t44.5 21q53 25 75 43t22 49q0 42 -43.5 71.5t-95.5 29.5q-56 0 -95 -27q-29 -20 -80 -83q-9 -12 -25 -12q-11 0 -19 6l-108 82q-10 7 -12 20t5 23q122 192 349 192q129 0 238.5 -89.5t109.5 -214.5zM768 1280q-130 0 -248.5 -51t-204 -136.5
+t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5
+t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="_628" unicode="&#xf29d;" horiz-adv-x="1408" 
+d="M366 1225q-64 0 -110 45.5t-46 110.5q0 64 46 109.5t110 45.5t109.5 -45.5t45.5 -109.5q0 -65 -45.5 -110.5t-109.5 -45.5zM917 583q0 -50 -30 -67.5t-63.5 -6.5t-47.5 34l-367 438q-7 12 -14 15.5t-11 1.5l-3 -3q-7 -8 4 -21l122 -139l1 -354l-161 -457
+q-67 -192 -92 -234q-15 -26 -28 -32q-50 -26 -103 -1q-29 13 -41.5 43t-9.5 57q2 17 197 618l5 416l-85 -164l35 -222q4 -24 -1 -42t-14 -27.5t-19 -16t-17 -7.5l-7 -2q-19 -3 -34.5 3t-24 16t-14 22t-7.5 19.5t-2 9.5l-46 299l211 381q23 34 113 34q75 0 107 -40l424 -521
+q7 -5 14 -17l3 -3l-1 -1q7 -13 7 -29zM514 433q43 -113 88.5 -225t69.5 -168l24 -55q36 -93 42 -125q11 -70 -36 -97q-35 -22 -66 -16t-51 22t-29 35h-1q-6 16 -8 25l-124 351zM1338 -159q31 -49 31 -57q0 -5 -3 -7q-9 -5 -14.5 0.5t-15.5 26t-16 30.5q-114 172 -423 661
+q3 -1 7 1t7 4l3 2q11 9 11 17z" />
+    <glyph glyph-name="_629" unicode="&#xf29e;" horiz-adv-x="2304" 
+d="M504 542h171l-1 265zM1530 641q0 87 -50.5 140t-146.5 53h-54v-388h52q91 0 145 57t54 138zM956 1018l1 -756q0 -14 -9.5 -24t-23.5 -10h-216q-14 0 -23.5 10t-9.5 24v62h-291l-55 -81q-10 -15 -28 -15h-267q-21 0 -30.5 18t3.5 35l556 757q9 14 27 14h332q14 0 24 -10
+t10 -24zM1783 641q0 -193 -125.5 -303t-324.5 -110h-270q-14 0 -24 10t-10 24v756q0 14 10 24t24 10h268q200 0 326 -109t126 -302zM1939 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5
+t-7.5 60t-20 91.5t-41 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2123 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-45 -108t-74 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5
+h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2304 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66 104.5t41 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96
+t9.5 -70.5z" />
+    <glyph glyph-name="uniF2A0" unicode="&#xf2a0;" horiz-adv-x="1408" 
+d="M617 -153q0 11 -13 58t-31 107t-20 69q-1 4 -5 26.5t-8.5 36t-13.5 21.5q-15 14 -51 14q-23 0 -70 -5.5t-71 -5.5q-34 0 -47 11q-6 5 -11 15.5t-7.5 20t-6.5 24t-5 18.5q-37 128 -37 255t37 255q1 4 5 18.5t6.5 24t7.5 20t11 15.5q13 11 47 11q24 0 71 -5.5t70 -5.5
+q36 0 51 14q9 8 13.5 21.5t8.5 36t5 26.5q2 9 20 69t31 107t13 58q0 22 -43.5 52.5t-75.5 42.5q-20 8 -45 8q-34 0 -98 -18q-57 -17 -96.5 -40.5t-71 -66t-46 -70t-45.5 -94.5q-6 -12 -9 -19q-49 -107 -68 -216t-19 -244t19 -244t68 -216q56 -122 83 -161q63 -91 179 -127
+l6 -2q64 -18 98 -18q25 0 45 8q32 12 75.5 42.5t43.5 52.5zM776 760q-26 0 -45 19t-19 45.5t19 45.5q37 37 37 90q0 52 -37 91q-19 19 -19 45t19 45t45 19t45 -19q75 -75 75 -181t-75 -181q-21 -19 -45 -19zM957 579q-27 0 -45 19q-19 19 -19 45t19 45q112 114 112 272
+t-112 272q-19 19 -19 45t19 45t45 19t45 -19q150 -150 150 -362t-150 -362q-18 -19 -45 -19zM1138 398q-27 0 -45 19q-19 19 -19 45t19 45q90 91 138.5 208t48.5 245t-48.5 245t-138.5 208q-19 19 -19 45t19 45t45 19t45 -19q109 -109 167 -249t58 -294t-58 -294t-167 -249
+q-18 -19 -45 -19z" />
+    <glyph glyph-name="uniF2A1" unicode="&#xf2a1;" horiz-adv-x="2176" 
+d="M192 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 352
+q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 864
+q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 1376q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 192q0 -80 -56 -136
+t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 1216q0 -80 -56 -136t-136 -56
+t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 192q0 -80 -56 -136t-136 -56t-136 56
+t-56 136t56 136t136 56t136 -56t56 -136zM1664 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136
+t56 136t136 56t136 -56t56 -136zM2176 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136z" />
+    <glyph glyph-name="uniF2A2" unicode="&#xf2a2;" horiz-adv-x="1792" 
+d="M128 -192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM320 0q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM365 365l256 -256l-90 -90l-256 256zM704 384q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45z
+M1411 704q0 -59 -11.5 -108.5t-37.5 -93.5t-44 -67.5t-53 -64.5q-31 -35 -45.5 -54t-33.5 -50t-26.5 -64t-7.5 -74q0 -159 -112.5 -271.5t-271.5 -112.5q-26 0 -45 19t-19 45t19 45t45 19q106 0 181 75t75 181q0 57 11.5 105.5t37 91t43.5 66.5t52 63q40 46 59.5 72
+t37.5 74.5t18 103.5q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM896 576q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45
+t45 19t45 -19t19 -45zM1184 704q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 93 -65.5 158.5t-158.5 65.5q-92 0 -158 -65.5t-66 -158.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 146 103 249t249 103t249 -103t103 -249zM1578 993q10 -25 -1 -49t-36 -34q-9 -4 -23 -4
+q-19 0 -35.5 11t-23.5 30q-68 178 -224 295q-21 16 -25 42t12 47q17 21 43 25t47 -12q183 -137 266 -351zM1788 1074q9 -25 -1.5 -49t-35.5 -34q-11 -4 -23 -4q-44 0 -60 41q-92 238 -297 393q-22 16 -25.5 42t12.5 47q16 22 42 25.5t47 -12.5q235 -175 341 -449z" />
+    <glyph glyph-name="uniF2A3" unicode="&#xf2a3;" horiz-adv-x="2304" 
+d="M1032 576q-59 2 -84 55q-17 34 -48 53.5t-68 19.5q-53 0 -90.5 -37.5t-37.5 -90.5q0 -56 36 -89l10 -8q34 -31 82 -31q37 0 68 19.5t48 53.5q25 53 84 55zM1600 704q0 56 -36 89l-10 8q-34 31 -82 31q-37 0 -68 -19.5t-48 -53.5q-25 -53 -84 -55q59 -2 84 -55
+q17 -34 48 -53.5t68 -19.5q53 0 90.5 37.5t37.5 90.5zM1174 925q-17 -35 -55 -48t-73 4q-62 31 -134 31q-51 0 -99 -17q3 0 9.5 0.5t9.5 0.5q92 0 170.5 -50t118.5 -133q17 -36 3.5 -73.5t-49.5 -54.5q-18 -9 -39 -9q21 0 39 -9q36 -17 49.5 -54.5t-3.5 -73.5
+q-40 -83 -118.5 -133t-170.5 -50h-6q-16 2 -44 4l-290 27l-239 -120q-14 -7 -29 -7q-40 0 -57 35l-160 320q-11 23 -4 47.5t29 37.5l209 119l148 267q17 155 91.5 291.5t195.5 236.5q31 25 70.5 21.5t64.5 -34.5t21.5 -70t-34.5 -65q-70 -59 -117 -128q123 84 267 101
+q40 5 71.5 -19t35.5 -64q5 -40 -19 -71.5t-64 -35.5q-84 -10 -159 -55q46 10 99 10q115 0 218 -50q36 -18 49 -55.5t-5 -73.5zM2137 1085l160 -320q11 -23 4 -47.5t-29 -37.5l-209 -119l-148 -267q-17 -155 -91.5 -291.5t-195.5 -236.5q-26 -22 -61 -22q-45 0 -74 35
+q-25 31 -21.5 70t34.5 65q70 59 117 128q-123 -84 -267 -101q-4 -1 -12 -1q-36 0 -63.5 24t-31.5 60q-5 40 19 71.5t64 35.5q84 10 159 55q-46 -10 -99 -10q-115 0 -218 50q-36 18 -49 55.5t5 73.5q17 35 55 48t73 -4q62 -31 134 -31q51 0 99 17q-3 0 -9.5 -0.5t-9.5 -0.5
+q-92 0 -170.5 50t-118.5 133q-17 36 -3.5 73.5t49.5 54.5q18 9 39 9q-21 0 -39 9q-36 17 -49.5 54.5t3.5 73.5q40 83 118.5 133t170.5 50h6h1q14 -2 42 -4l291 -27l239 120q14 7 29 7q40 0 57 -35z" />
+    <glyph glyph-name="uniF2A4" unicode="&#xf2a4;" horiz-adv-x="1792" 
+d="M1056 704q0 -26 19 -45t45 -19t45 19t19 45q0 146 -103 249t-249 103t-249 -103t-103 -249q0 -26 19 -45t45 -19t45 19t19 45q0 93 66 158.5t158 65.5t158 -65.5t66 -158.5zM835 1280q-117 0 -223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5q0 -26 19 -45t45 -19t45 19
+t19 45q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -55 -18 -103.5t-37.5 -74.5t-59.5 -72q-34 -39 -52 -63t-43.5 -66.5t-37 -91t-11.5 -105.5q0 -106 -75 -181t-181 -75q-26 0 -45 -19t-19 -45t19 -45t45 -19q159 0 271.5 112.5t112.5 271.5q0 41 7.5 74
+t26.5 64t33.5 50t45.5 54q35 41 53 64.5t44 67.5t37.5 93.5t11.5 108.5q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5zM591 561l226 -226l-579 -579q-12 -12 -29 -12t-29 12l-168 168q-12 12 -12 29t12 29zM1612 1524l168 -168q12 -12 12 -29t-12 -30l-233 -233
+l-26 -25l-71 -71q-66 153 -195 258l91 91l207 207q13 12 30 12t29 -12z" />
+    <glyph glyph-name="uniF2A5" unicode="&#xf2a5;" 
+d="M866 1021q0 -27 -13 -94q-11 -50 -31.5 -150t-30.5 -150q-2 -11 -4.5 -12.5t-13.5 -2.5q-20 -2 -31 -2q-58 0 -84 49.5t-26 113.5q0 88 35 174t103 124q28 14 51 14q28 0 36.5 -16.5t8.5 -47.5zM1352 597q0 14 -39 75.5t-52 66.5q-21 8 -34 8q-91 0 -226 -77l-2 2
+q3 22 27.5 135t24.5 178q0 233 -242 233q-24 0 -68 -6q-94 -17 -168.5 -89.5t-111.5 -166.5t-37 -189q0 -146 80.5 -225t227.5 -79q25 0 25 -3t-1 -5q-4 -34 -26 -117q-14 -52 -51.5 -101t-82.5 -49q-42 0 -42 47q0 24 10.5 47.5t25 39.5t29.5 28.5t26 20t11 8.5q0 3 -7 10
+q-24 22 -58.5 36.5t-65.5 14.5q-35 0 -63.5 -34t-41 -75t-12.5 -75q0 -88 51.5 -142t138.5 -54q82 0 155 53t117.5 126t65.5 153q6 22 15.5 66.5t14.5 66.5q3 12 14 18q118 60 227 60q48 0 127 -18q1 -1 4 -1q5 0 9.5 4.5t4.5 8.5zM1536 1120v-960q0 -119 -84.5 -203.5
+t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="uniF2A6" unicode="&#xf2a6;" horiz-adv-x="1535" 
+d="M744 1231q0 24 -2 38.5t-8.5 30t-21 23t-37.5 7.5q-39 0 -78 -23q-105 -58 -159 -190.5t-54 -269.5q0 -44 8.5 -85.5t26.5 -80.5t52.5 -62.5t81.5 -23.5q4 0 18 -0.5t20 0t16 3t15 8.5t7 16q16 77 48 231.5t48 231.5q19 91 19 146zM1498 575q0 -7 -7.5 -13.5t-15.5 -6.5
+l-6 1q-22 3 -62 11t-72 12.5t-63 4.5q-167 0 -351 -93q-15 -8 -21 -27q-10 -36 -24.5 -105.5t-22.5 -100.5q-23 -91 -70 -179.5t-112.5 -164.5t-154.5 -123t-185 -47q-135 0 -214.5 83.5t-79.5 219.5q0 53 19.5 117t63 116.5t97.5 52.5q38 0 120 -33.5t83 -61.5
+q0 -1 -16.5 -12.5t-39.5 -31t-46 -44.5t-39 -61t-16 -74q0 -33 16.5 -53t48.5 -20q45 0 85 31.5t66.5 78t48 105.5t32.5 107t16 90v9q0 2 -3.5 3.5t-8.5 1.5h-10t-10 -0.5t-6 -0.5q-227 0 -352 122.5t-125 348.5q0 108 34.5 221t96 210t156 167.5t204.5 89.5q52 9 106 9
+q374 0 374 -360q0 -98 -38 -273t-43 -211l3 -3q101 57 182.5 88t167.5 31q22 0 53 -13q19 -7 80 -102.5t61 -116.5z" />
+    <glyph glyph-name="uniF2A7" unicode="&#xf2a7;" horiz-adv-x="1664" 
+d="M831 863q32 0 59 -18l222 -148q61 -40 110 -97l146 -170q40 -46 29 -106l-72 -413q-6 -32 -29.5 -53.5t-55.5 -25.5l-527 -56l-352 -32h-9q-39 0 -67.5 28t-28.5 68q0 37 27 64t65 32l260 32h-448q-41 0 -69.5 30t-26.5 71q2 39 32 65t69 26l442 1l-521 64q-41 5 -66 37
+t-19 73q6 35 34.5 57.5t65.5 22.5h10l481 -60l-351 94q-38 10 -62 41.5t-18 68.5q6 36 33 58.5t62 22.5q6 0 20 -2l448 -96l217 -37q1 0 3 -0.5t3 -0.5q23 0 30.5 23t-12.5 36l-186 125q-35 23 -42 63.5t18 73.5q27 38 76 38zM761 661l186 -125l-218 37l-5 2l-36 38
+l-238 262q-1 1 -2.5 3.5t-2.5 3.5q-24 31 -18.5 70t37.5 64q31 23 68 17.5t64 -33.5l142 -147q-2 -1 -5 -3.5t-4 -4.5q-32 -45 -23 -99t55 -85zM1648 1115l15 -266q4 -73 -11 -147l-48 -219q-12 -59 -67 -87l-106 -54q2 62 -39 109l-146 170q-53 61 -117 103l-222 148
+q-34 23 -76 23q-51 0 -88 -37l-235 312q-25 33 -18 73.5t41 63.5q33 22 71.5 14t62.5 -40l266 -352l-262 455q-21 35 -10.5 75t47.5 59q35 18 72.5 6t57.5 -46l241 -420l-136 337q-15 35 -4.5 74t44.5 56q37 19 76 6t56 -51l193 -415l101 -196q8 -15 23 -17.5t27 7.5t11 26
+l-12 224q-2 41 26 71t69 31q39 0 67 -28.5t30 -67.5z" />
+    <glyph glyph-name="uniF2A8" unicode="&#xf2a8;" horiz-adv-x="1792" 
+d="M335 180q-2 0 -6 2q-86 57 -168.5 145t-139.5 180q-21 30 -21 69q0 9 2 19t4 18t7 18t8.5 16t10.5 17t10 15t12 15.5t11 14.5q184 251 452 365q-110 198 -110 211q0 19 17 29q116 64 128 64q18 0 28 -16l124 -229q92 19 192 19q266 0 497.5 -137.5t378.5 -369.5
+q20 -31 20 -69t-20 -69q-91 -142 -218.5 -253.5t-278.5 -175.5q110 -198 110 -211q0 -20 -17 -29q-116 -64 -127 -64q-19 0 -29 16l-124 229l-64 119l-444 820l7 7q-58 -24 -99 -47q3 -5 127 -234t243 -449t119 -223q0 -7 -9 -9q-13 -3 -72 -3q-57 0 -60 7l-456 841
+q-39 -28 -82 -68q24 -43 214 -393.5t190 -354.5q0 -10 -11 -10q-14 0 -82.5 22t-72.5 28l-106 197l-224 413q-44 -53 -78 -106q2 -3 18 -25t23 -34l176 -327q0 -10 -10 -10zM1165 282l49 -91q273 111 450 385q-180 277 -459 389q67 -64 103 -148.5t36 -176.5
+q0 -106 -47 -200.5t-132 -157.5zM848 896q0 -20 14 -34t34 -14q86 0 147 -61t61 -147q0 -20 14 -34t34 -14t34 14t14 34q0 126 -89 215t-215 89q-20 0 -34 -14t-14 -34zM1214 961l-9 4l7 -7z" />
+    <glyph glyph-name="uniF2A9" unicode="&#xf2a9;" horiz-adv-x="1280" 
+d="M1050 430q0 -215 -147 -374q-148 -161 -378 -161q-232 0 -378 161q-147 159 -147 374q0 147 68 270.5t189 196.5t268 73q96 0 182 -31q-32 -62 -39 -126q-66 28 -143 28q-167 0 -280.5 -123t-113.5 -291q0 -170 112.5 -288.5t281.5 -118.5t281 118.5t112 288.5
+q0 89 -32 166q66 13 123 49q41 -98 41 -212zM846 619q0 -192 -79.5 -345t-238.5 -253l-14 -1q-29 0 -62 5q83 32 146.5 102.5t99.5 154.5t58.5 189t30 192.5t7.5 178.5q0 69 -3 103q55 -160 55 -326zM791 947v-2q-73 214 -206 440q88 -59 142.5 -186.5t63.5 -251.5z
+M1035 744q-83 0 -160 75q218 120 290 247q19 37 21 56q-42 -94 -139.5 -166.5t-204.5 -97.5q-35 54 -35 113q0 37 17 79t43 68q46 44 157 74q59 16 106 58.5t74 100.5q74 -105 74 -253q0 -109 -24 -170q-32 -77 -88.5 -130.5t-130.5 -53.5z" />
+    <glyph glyph-name="uniF2AA" unicode="&#xf2aa;" 
+d="M1050 495q0 78 -28 147q-41 -25 -85 -34q22 -50 22 -114q0 -117 -77 -198.5t-193 -81.5t-193.5 81.5t-77.5 198.5q0 115 78 199.5t193 84.5q53 0 98 -19q4 43 27 87q-60 21 -125 21q-154 0 -257.5 -108.5t-103.5 -263.5t103.5 -261t257.5 -106t257.5 106.5t103.5 260.5z
+M872 850q2 -24 2 -71q0 -63 -5 -123t-20.5 -132.5t-40.5 -130t-68.5 -106t-100.5 -70.5q21 -3 42 -3h10q219 139 219 411q0 116 -38 225zM872 850q-4 80 -44 171.5t-98 130.5q92 -156 142 -302zM1207 955q0 102 -51 174q-41 -86 -124 -109q-69 -19 -109 -53.5t-40 -99.5
+q0 -40 24 -77q74 17 140.5 67t95.5 115q-4 -52 -74.5 -111.5t-138.5 -97.5q52 -52 110 -52q51 0 90 37t60 90q17 42 17 117zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5
+t84.5 -203.5z" />
+    <glyph glyph-name="uniF2AB" unicode="&#xf2ab;" 
+d="M1279 388q0 22 -22 27q-67 15 -118 59t-80 108q-7 19 -7 25q0 15 19.5 26t43 17t43 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-12 0 -32 -8t-31 -8q-4 0 -12 2q5 95 5 114q0 79 -17 114q-36 78 -103 121.5t-152 43.5q-199 0 -275 -165q-17 -35 -17 -114q0 -19 5 -114
+q-4 -2 -14 -2q-12 0 -32 7.5t-30 7.5q-21 0 -38.5 -12t-17.5 -32q0 -21 19.5 -35.5t43 -20.5t43 -17t19.5 -26q0 -6 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -46 137 -68q2 -5 6 -26t11.5 -30.5t23.5 -9.5q12 0 37.5 4.5t39.5 4.5q35 0 67 -15t54 -32.5t57.5 -32.5
+t76.5 -15q43 0 79 15t57.5 32.5t53.5 32.5t67 15q14 0 39.5 -4t38.5 -4q16 0 23 10t11 30t6 25q137 22 137 68zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5
+t103 -385.5z" />
+    <glyph glyph-name="uniF2AC" unicode="&#xf2ac;" horiz-adv-x="1664" 
+d="M848 1408q134 1 240.5 -68.5t163.5 -192.5q27 -58 27 -179q0 -47 -9 -191q14 -7 28 -7q18 0 51 13.5t51 13.5q29 0 56 -18t27 -46q0 -32 -31.5 -54t-69 -31.5t-69 -29t-31.5 -47.5q0 -15 12 -43q37 -82 102.5 -150t144.5 -101q28 -12 80 -23q28 -6 28 -35
+q0 -70 -219 -103q-7 -11 -11 -39t-14 -46.5t-33 -18.5q-20 0 -62 6.5t-64 6.5q-37 0 -62 -5q-32 -5 -63 -22.5t-58 -38t-58 -40.5t-76 -33.5t-99 -13.5q-52 0 -96.5 13.5t-75 33.5t-57.5 40.5t-58 38t-62 22.5q-26 5 -63 5q-24 0 -65.5 -7.5t-58.5 -7.5q-25 0 -35 18.5
+t-14 47.5t-11 40q-219 33 -219 103q0 29 28 35q52 11 80 23q78 32 144.5 101t102.5 150q12 28 12 43q0 28 -31.5 47.5t-69.5 29.5t-69.5 31.5t-31.5 52.5q0 27 26 45.5t55 18.5q15 0 48 -13t53 -13q18 0 32 7q-9 142 -9 190q0 122 27 180q64 137 172 198t264 63z" />
+    <glyph glyph-name="uniF2AD" unicode="&#xf2ad;" 
+d="M1280 388q0 22 -22 27q-67 14 -118 58t-80 109q-7 14 -7 25q0 15 19.5 26t42.5 17t42.5 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-11 0 -31 -8t-32 -8q-4 0 -12 2q5 63 5 115q0 78 -17 114q-36 78 -102.5 121.5t-152.5 43.5q-198 0 -275 -165q-18 -38 -18 -115
+q0 -38 6 -114q-10 -2 -15 -2q-11 0 -31.5 8t-30.5 8q-20 0 -37.5 -12.5t-17.5 -32.5q0 -21 19.5 -35.5t42.5 -20.5t42.5 -17t19.5 -26q0 -11 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -47 138 -69q2 -5 6 -26t11 -30.5t23 -9.5q13 0 38.5 5t38.5 5q35 0 67.5 -15
+t54.5 -32.5t57.5 -32.5t76.5 -15q43 0 79 15t57.5 32.5t54 32.5t67.5 15q13 0 39 -4.5t39 -4.5q15 0 22.5 9.5t11.5 31t5 24.5q138 22 138 69zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960
+q119 0 203.5 -84.5t84.5 -203.5z" />
+    <glyph glyph-name="uniF2AE" unicode="&#xf2ae;" horiz-adv-x="2304" 
+d="M2304 1536q-69 -46 -125 -92t-89 -81t-59.5 -71.5t-37.5 -57.5t-22 -44.5t-14 -29.5q-10 -18 -35.5 -136.5t-48.5 -164.5q-15 -29 -50 -60.5t-67.5 -50.5t-72.5 -41t-48 -28q-47 -31 -151 -231q-341 14 -630 -158q-92 -53 -303 -179q47 16 86 31t55 22l15 7
+q71 27 163 64.5t133.5 53.5t108 34.5t142.5 31.5q186 31 465 -7q1 0 10 -3q11 -6 14 -17t-3 -22l-194 -345q-15 -29 -47 -22q-128 24 -354 24q-146 0 -402 -44.5t-392 -46.5q-82 -1 -149 13t-107 37t-61 40t-33 34l-1 1v2q0 6 6 6q138 0 371 55q192 366 374.5 524t383.5 158
+q5 0 14.5 -0.5t38 -5t55 -12t61.5 -24.5t63 -39.5t54 -59t40 -82.5l102 177q2 4 21 42.5t44.5 86.5t61 109.5t84 133.5t100.5 137q66 82 128 141.5t121.5 96.5t92.5 53.5t88 39.5z" />
+    <glyph glyph-name="uniF2B0" unicode="&#xf2b0;" 
+d="M1322 640q0 -45 -5 -76l-236 14l224 -78q-19 -73 -58 -141l-214 103l177 -158q-44 -61 -107 -108l-157 178l103 -215q-61 -37 -140 -59l-79 228l14 -240q-38 -6 -76 -6t-76 6l14 238l-78 -226q-74 19 -140 59l103 215l-157 -178q-59 43 -108 108l178 158l-214 -104
+q-39 69 -58 141l224 79l-237 -14q-5 42 -5 76q0 35 5 77l238 -14l-225 79q19 73 58 140l214 -104l-177 159q46 61 107 108l158 -178l-103 215q67 39 140 58l77 -224l-13 236q36 6 75 6q38 0 76 -6l-14 -237l78 225q74 -19 140 -59l-103 -214l158 178q61 -47 107 -108
+l-177 -159l213 104q37 -62 58 -141l-224 -78l237 14q5 -31 5 -77zM1352 640q0 160 -78.5 295.5t-213 214t-292.5 78.5q-119 0 -227 -46.5t-186.5 -125t-124.5 -187.5t-46 -229q0 -119 46 -228t124.5 -187.5t186.5 -125t227 -46.5q158 0 292.5 78.5t213 214t78.5 294.5z
+M1425 1023v-766l-657 -383l-657 383v766l657 383zM768 -183l708 412v823l-708 411l-708 -411v-823zM1536 1088v-896l-768 -448l-768 448v896l768 448z" />
+    <glyph glyph-name="uniF2B1" unicode="&#xf2b1;" horiz-adv-x="1664" 
+d="M339 1318h691l-26 -72h-665q-110 0 -188.5 -79t-78.5 -189v-771q0 -95 60.5 -169.5t153.5 -93.5q23 -5 98 -5v-72h-45q-140 0 -239.5 100t-99.5 240v771q0 140 99.5 240t239.5 100zM1190 1536h247l-482 -1294q-23 -61 -40.5 -103.5t-45 -98t-54 -93.5t-64.5 -78.5
+t-79.5 -65t-95.5 -41t-116 -18.5v195q163 26 220 182q20 52 20 105q0 54 -20 106l-285 733h228l187 -585zM1664 978v-1111h-795q37 55 45 73h678v1038q0 85 -49.5 155t-129.5 99l25 67q101 -34 163.5 -123.5t62.5 -197.5z" />
+    <glyph glyph-name="uniF2B2" unicode="&#xf2b2;" horiz-adv-x="1792" 
+d="M852 1227q0 -29 -17 -52.5t-45 -23.5t-45 23.5t-17 52.5t17 52.5t45 23.5t45 -23.5t17 -52.5zM688 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50 -21.5t-20 -51.5v-114q0 -30 20.5 -52t49.5 -22q30 0 50.5 22t20.5 52zM860 -149v114q0 30 -20 51.5t-50 21.5t-50.5 -21.5
+t-20.5 -51.5v-114q0 -30 20.5 -52t50.5 -22q29 0 49.5 22t20.5 52zM1034 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50.5 -21.5t-20.5 -51.5v-114q0 -30 20.5 -52t50.5 -22t50.5 22t20.5 52zM1208 -149v114q0 30 -20.5 51.5t-50.5 21.5t-50.5 -21.5t-20.5 -51.5v-114
+q0 -30 20.5 -52t50.5 -22t50.5 22t20.5 52zM1476 535q-84 -160 -232 -259.5t-323 -99.5q-123 0 -229.5 51.5t-178.5 137t-113 197.5t-41 232q0 88 21 174q-104 -175 -104 -390q0 -162 65 -312t185 -251q30 57 91 57q56 0 86 -50q32 50 87 50q56 0 86 -50q32 50 87 50t87 -50
+q30 50 86 50q28 0 52.5 -15.5t37.5 -40.5q112 94 177 231.5t73 287.5zM1326 564q0 75 -72 75q-17 0 -47 -6q-95 -19 -149 -19q-226 0 -226 243q0 86 30 204q-83 -127 -83 -275q0 -150 89 -260.5t235 -110.5q111 0 210 70q13 48 13 79zM884 1223q0 50 -32 89.5t-81 39.5
+t-81 -39.5t-32 -89.5q0 -51 31.5 -90.5t81.5 -39.5t81.5 39.5t31.5 90.5zM1513 884q0 96 -37.5 179t-113 137t-173.5 54q-77 0 -149 -35t-127 -94q-48 -159 -48 -268q0 -104 45.5 -157t147.5 -53q53 0 142 19q36 6 53 6q51 0 77.5 -28t26.5 -80q0 -26 -4 -46
+q75 68 117.5 165.5t42.5 200.5zM1792 667q0 -111 -33.5 -249.5t-93.5 -204.5q-58 -64 -195 -142.5t-228 -104.5l-4 -1v-114q0 -43 -29.5 -75t-72.5 -32q-56 0 -86 50q-32 -50 -87 -50t-87 50q-30 -50 -86 -50q-55 0 -87 50q-30 -50 -86 -50q-47 0 -75 33.5t-28 81.5
+q-90 -68 -198 -68q-118 0 -211 80q54 1 106 20q-113 31 -182 127q32 -7 71 -7q89 0 164 46q-192 192 -240 306q-24 56 -24 160q0 57 9 125.5t31.5 146.5t55 141t86.5 105t120 42q59 0 81 -52q19 29 42 54q2 3 12 13t13 16q10 15 23 38t25 42t28 39q87 111 211.5 177
+t260.5 66q35 0 62 -4q59 64 146 64q83 0 140 -57q5 -5 5 -12q0 -5 -6 -13.5t-12.5 -16t-16 -17l-10.5 -10.5q17 -6 36 -18t19 -24q0 -6 -16 -25q157 -138 197 -378q25 30 60 30q45 0 100 -49q90 -80 90 -279z" />
+    <glyph glyph-name="uniF2B3" unicode="&#xf2b3;" 
+d="M917 631q0 33 -6 64h-362v-132h217q-12 -76 -74.5 -120.5t-142.5 -44.5q-99 0 -169 71.5t-70 170.5t70 170.5t169 71.5q93 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585h109v110
+h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
+    <glyph glyph-name="uniF2B4" unicode="&#xf2b4;" 
+d="M1536 1024v-839q0 -48 -49 -62q-174 -52 -338 -52q-73 0 -215.5 29.5t-227.5 29.5q-164 0 -370 -48v-338h-160v1368q-63 25 -101 81t-38 124q0 91 64 155t155 64t155 -64t64 -155q0 -68 -38 -124t-101 -81v-68q190 44 343 44q99 0 198 -15q14 -2 111.5 -22.5t149.5 -20.5
+q77 0 165 18q11 2 80 21t89 19q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="uniF2B5" unicode="&#xf2b5;" horiz-adv-x="2304" 
+d="M192 384q40 0 56 32t0 64t-56 32t-56 -32t0 -64t56 -32zM1665 442q-10 13 -38.5 50t-41.5 54t-38 49t-42.5 53t-40.5 47t-45 49l-125 -140q-83 -94 -208.5 -92t-205.5 98q-57 69 -56.5 158t58.5 157l177 206q-22 11 -51 16.5t-47.5 6t-56.5 -0.5t-49 -1q-92 0 -158 -66
+l-158 -158h-155v-544q5 0 21 0.5t22 0t19.5 -2t20.5 -4.5t17.5 -8.5t18.5 -13.5l297 -292q115 -111 227 -111q78 0 125 47q57 -20 112.5 8t72.5 85q74 -6 127 44q20 18 36 45.5t14 50.5q10 -10 43 -10q43 0 77 21t49.5 53t12 71.5t-30.5 73.5zM1824 384h96v512h-93l-157 180
+q-66 76 -169 76h-167q-89 0 -146 -67l-209 -243q-28 -33 -28 -75t27 -75q43 -51 110 -52t111 49l193 218q25 23 53.5 21.5t47 -27t8.5 -56.5q16 -19 56 -63t60 -68q29 -36 82.5 -105.5t64.5 -84.5q52 -66 60 -140zM2112 384q40 0 56 32t0 64t-56 32t-56 -32t0 -64t56 -32z
+M2304 960v-640q0 -26 -19 -45t-45 -19h-434q-27 -65 -82 -106.5t-125 -51.5q-33 -48 -80.5 -81.5t-102.5 -45.5q-42 -53 -104.5 -81.5t-128.5 -24.5q-60 -34 -126 -39.5t-127.5 14t-117 53.5t-103.5 81l-287 282h-358q-26 0 -45 19t-19 45v672q0 26 19 45t45 19h421
+q14 14 47 48t47.5 48t44 40t50.5 37.5t51 25.5t62 19.5t68 5.5h117q99 0 181 -56q82 56 181 56h167q35 0 67 -6t56.5 -14.5t51.5 -26.5t44.5 -31t43 -39.5t39 -42t41 -48t41.5 -48.5h355q26 0 45 -19t19 -45z" />
+    <glyph glyph-name="uniF2B6" unicode="&#xf2b6;" horiz-adv-x="1792" 
+d="M1792 882v-978q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v978q0 15 11 24q8 7 39 34.5t41.5 36t45.5 37.5t70 55.5t96 73t143.5 107t192.5 140.5q5 4 52.5 40t71.5 52.5t64 35t69 18.5t69 -18.5t65 -35.5t71 -52t52 -40q110 -80 192.5 -140.5t143.5 -107
+t96 -73t70 -55.5t45.5 -37.5t41.5 -36t39 -34.5q11 -9 11 -24zM1228 297q263 191 345 252q11 8 12.5 20.5t-6.5 23.5l-38 52q-8 11 -21 12.5t-24 -6.5q-231 -169 -343 -250q-5 -3 -52 -39t-71.5 -52.5t-64.5 -35t-69 -18.5t-69 18.5t-64.5 35t-71.5 52.5t-52 39
+q-186 134 -343 250q-11 8 -24 6.5t-21 -12.5l-38 -52q-8 -11 -6.5 -23.5t12.5 -20.5q82 -61 345 -252q10 -8 50 -38t65 -47t64 -39.5t77.5 -33.5t75.5 -11t75.5 11t79 34.5t64.5 39.5t65 47.5t48 36.5z" />
+    <glyph glyph-name="uniF2B7" unicode="&#xf2b7;" horiz-adv-x="1792" 
+d="M1474 623l39 -51q8 -11 6.5 -23.5t-11.5 -20.5q-43 -34 -126.5 -98.5t-146.5 -113t-67 -51.5q-39 -32 -60 -48t-60.5 -41t-76.5 -36.5t-74 -11.5h-1h-1q-37 0 -74 11.5t-76 36.5t-61 41.5t-60 47.5q-5 4 -65 50.5t-143.5 111t-122.5 94.5q-11 8 -12.5 20.5t6.5 23.5
+l37 52q8 11 21.5 13t24.5 -7q94 -73 306 -236q5 -4 43.5 -35t60.5 -46.5t56.5 -32.5t58.5 -17h1h1q24 0 58.5 17t56.5 32.5t60.5 46.5t43.5 35q258 198 313 242q11 8 24 6.5t21 -12.5zM1664 -96v928q-90 83 -159 139q-91 74 -389 304q-3 2 -43 35t-61 48t-56 32.5t-59 17.5
+h-1h-1q-24 0 -59 -17.5t-56 -32.5t-61 -48t-43 -35q-215 -166 -315.5 -245.5t-129.5 -104t-82 -74.5q-14 -12 -21 -19v-928q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 832v-928q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v928q0 56 41 94
+q123 114 350 290.5t233 181.5q36 30 59 47.5t61.5 42t76 36.5t74.5 12h1h1q37 0 74.5 -12t76 -36.5t61.5 -42t59 -47.5q43 -36 156 -122t226 -177t201 -173q41 -38 41 -94z" />
+    <glyph glyph-name="uniF2B8" unicode="&#xf2b8;" 
+d="M330 1l202 -214l-34 236l-216 213zM556 -225l274 218l-11 245l-300 -215zM245 413l227 -213l-48 327l-245 204zM495 189l317 214l-14 324l-352 -200zM843 178l95 -80l-2 239l-103 79q0 -1 1 -8.5t0 -12t-5 -7.5l-78 -52l85 -70q7 -6 7 -88zM138 930l256 -200l-68 465
+l-279 173zM1173 267l15 234l-230 -164l2 -240zM417 722l373 194l-19 441l-423 -163zM1270 357l20 233l-226 142l-2 -105l144 -95q6 -4 4 -9l-7 -119zM1461 496l30 222l-179 -128l-20 -228zM1273 329l-71 49l-8 -117q0 -5 -4 -8l-234 -187q-7 -5 -14 0l-98 83l7 -161
+q0 -5 -4 -8l-293 -234q-4 -2 -6 -2q-8 2 -8 3l-228 242q-4 4 -59 277q-2 7 5 11l61 37q-94 86 -95 92l-72 351q-2 7 6 12l94 45q-133 100 -135 108l-96 466q-2 10 7 13l433 135q5 0 8 -1l317 -153q6 -4 6 -9l20 -463q0 -7 -6 -10l-118 -61l126 -85q5 -2 5 -8l5 -123l121 74
+q5 4 11 0l84 -56l3 110q0 6 5 9l206 126q6 3 11 0l245 -135q4 -4 5 -7t-6.5 -60t-17.5 -124.5t-10 -70.5q0 -5 -4 -7l-191 -153q-6 -5 -13 0z" />
+    <glyph glyph-name="uniF2B9" unicode="&#xf2b9;" horiz-adv-x="1664" 
+d="M1201 298q0 57 -5.5 107t-21 100.5t-39.5 86t-64 58t-91 22.5q-6 -4 -33.5 -20.5t-42.5 -24.5t-40.5 -20t-49 -17t-46.5 -5t-46.5 5t-49 17t-40.5 20t-42.5 24.5t-33.5 20.5q-51 0 -91 -22.5t-64 -58t-39.5 -86t-21 -100.5t-5.5 -107q0 -73 42 -121.5t103 -48.5h576
+q61 0 103 48.5t42 121.5zM1028 892q0 108 -76.5 184t-183.5 76t-183.5 -76t-76.5 -184q0 -107 76.5 -183t183.5 -76t183.5 76t76.5 183zM1664 352v-192q0 -14 -9 -23t-23 -9h-96v-224q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v1472q0 66 47 113t113 47h1216
+q66 0 113 -47t47 -113v-224h96q14 0 23 -9t9 -23v-192q0 -14 -9 -23t-23 -9h-96v-128h96q14 0 23 -9t9 -23v-192q0 -14 -9 -23t-23 -9h-96v-128h96q14 0 23 -9t9 -23z" />
+    <glyph glyph-name="uniF2BA" unicode="&#xf2ba;" horiz-adv-x="1664" 
+d="M1028 892q0 -107 -76.5 -183t-183.5 -76t-183.5 76t-76.5 183q0 108 76.5 184t183.5 76t183.5 -76t76.5 -184zM980 672q46 0 82.5 -17t60 -47.5t39.5 -67t24 -81t11.5 -82.5t3.5 -79q0 -67 -39.5 -118.5t-105.5 -51.5h-576q-66 0 -105.5 51.5t-39.5 118.5q0 48 4.5 93.5
+t18.5 98.5t36.5 91.5t63 64.5t93.5 26h5q7 -4 32 -19.5t35.5 -21t33 -17t37 -16t35 -9t39.5 -4.5t39.5 4.5t35 9t37 16t33 17t35.5 21t32 19.5zM1664 928q0 -13 -9.5 -22.5t-22.5 -9.5h-96v-128h96q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-96v-128h96
+q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-96v-224q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v1472q0 66 47 113t113 47h1216q66 0 113 -47t47 -113v-224h96q13 0 22.5 -9.5t9.5 -22.5v-192zM1408 -96v1472q0 13 -9.5 22.5t-22.5 9.5h-1216
+q-13 0 -22.5 -9.5t-9.5 -22.5v-1472q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5z" />
+    <glyph glyph-name="uniF2BB" unicode="&#xf2bb;" horiz-adv-x="2048" 
+d="M1024 405q0 64 -9 117.5t-29.5 103t-60.5 78t-97 28.5q-6 -4 -30 -18t-37.5 -21.5t-35.5 -17.5t-43 -14.5t-42 -4.5t-42 4.5t-43 14.5t-35.5 17.5t-37.5 21.5t-30 18q-57 0 -97 -28.5t-60.5 -78t-29.5 -103t-9 -117.5t37 -106.5t91 -42.5h512q54 0 91 42.5t37 106.5z
+M867 925q0 94 -66.5 160.5t-160.5 66.5t-160.5 -66.5t-66.5 -160.5t66.5 -160.5t160.5 -66.5t160.5 66.5t66.5 160.5zM1792 416v64q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1792 676v56q0 15 -10.5 25.5t-25.5 10.5h-568
+q-15 0 -25.5 -10.5t-10.5 -25.5v-56q0 -15 10.5 -25.5t25.5 -10.5h568q15 0 25.5 10.5t10.5 25.5zM1792 928v64q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM2048 1248v-1216q0 -66 -47 -113t-113 -47h-352v96q0 14 -9 23t-23 9
+h-64q-14 0 -23 -9t-9 -23v-96h-768v96q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-96h-352q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1728q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2BC" unicode="&#xf2bc;" horiz-adv-x="2048" 
+d="M1024 405q0 -64 -37 -106.5t-91 -42.5h-512q-54 0 -91 42.5t-37 106.5t9 117.5t29.5 103t60.5 78t97 28.5q6 -4 30 -18t37.5 -21.5t35.5 -17.5t43 -14.5t42 -4.5t42 4.5t43 14.5t35.5 17.5t37.5 21.5t30 18q57 0 97 -28.5t60.5 -78t29.5 -103t9 -117.5zM867 925
+q0 -94 -66.5 -160.5t-160.5 -66.5t-160.5 66.5t-66.5 160.5t66.5 160.5t160.5 66.5t160.5 -66.5t66.5 -160.5zM1792 480v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM1792 732v-56q0 -15 -10.5 -25.5t-25.5 -10.5h-568
+q-15 0 -25.5 10.5t-10.5 25.5v56q0 15 10.5 25.5t25.5 10.5h568q15 0 25.5 -10.5t10.5 -25.5zM1792 992v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM1920 32v1216q0 13 -9.5 22.5t-22.5 9.5h-1728q-13 0 -22.5 -9.5
+t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h352v96q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-96h768v96q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-96h352q13 0 22.5 9.5t9.5 22.5zM2048 1248v-1216q0 -66 -47 -113t-113 -47h-1728q-66 0 -113 47t-47 113v1216q0 66 47 113
+t113 47h1728q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2BD" unicode="&#xf2bd;" horiz-adv-x="1792" 
+d="M1523 197q-22 155 -87.5 257.5t-184.5 118.5q-67 -74 -159.5 -115.5t-195.5 -41.5t-195.5 41.5t-159.5 115.5q-119 -16 -184.5 -118.5t-87.5 -257.5q106 -150 271 -237.5t356 -87.5t356 87.5t271 237.5zM1280 896q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5
+t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1792 640q0 -182 -71 -347.5t-190.5 -286t-285.5 -191.5t-349 -71q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="uniF2BE" unicode="&#xf2be;" horiz-adv-x="1792" 
+d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348q0 -181 -70.5 -347t-190.5 -286t-286 -191.5t-349 -71.5t-349 71t-285.5 191.5t-190.5 286t-71 347.5t71 348t191 286t286 191t348 71zM1515 185q149 205 149 455q0 156 -61 298t-164 245t-245 164t-298 61t-298 -61
+t-245 -164t-164 -245t-61 -298q0 -250 149 -455q66 327 306 327q131 -128 313 -128t313 128q240 0 306 -327zM1280 832q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5z" />
+    <glyph glyph-name="uniF2C0" unicode="&#xf2c0;" 
+d="M1201 752q47 -14 89.5 -38t89 -73t79.5 -115.5t55 -172t22 -236.5q0 -154 -100 -263.5t-241 -109.5h-854q-141 0 -241 109.5t-100 263.5q0 131 22 236.5t55 172t79.5 115.5t89 73t89.5 38q-79 125 -79 272q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5
+t198.5 -40.5t163.5 -109.5t109.5 -163.5t40.5 -198.5q0 -147 -79 -272zM768 1408q-159 0 -271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5zM1195 -128q88 0 150.5 71.5t62.5 173.5q0 239 -78.5 377t-225.5 145
+q-145 -127 -336 -127t-336 127q-147 -7 -225.5 -145t-78.5 -377q0 -102 62.5 -173.5t150.5 -71.5h854z" />
+    <glyph glyph-name="uniF2C1" unicode="&#xf2c1;" horiz-adv-x="1280" 
+d="M1024 278q0 -64 -37 -107t-91 -43h-512q-54 0 -91 43t-37 107t9 118t29.5 104t61 78.5t96.5 28.5q80 -75 188 -75t188 75q56 0 96.5 -28.5t61 -78.5t29.5 -104t9 -118zM870 797q0 -94 -67.5 -160.5t-162.5 -66.5t-162.5 66.5t-67.5 160.5t67.5 160.5t162.5 66.5
+t162.5 -66.5t67.5 -160.5zM1152 -96v1376h-1024v-1376q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1280 1376v-1472q0 -66 -47 -113t-113 -47h-960q-66 0 -113 47t-47 113v1472q0 66 47 113t113 47h352v-96q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v96h352
+q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2C2" unicode="&#xf2c2;" horiz-adv-x="2048" 
+d="M896 324q0 54 -7.5 100.5t-24.5 90t-51 68.5t-81 25q-64 -64 -156 -64t-156 64q-47 0 -81 -25t-51 -68.5t-24.5 -90t-7.5 -100.5q0 -55 31.5 -93.5t75.5 -38.5h426q44 0 75.5 38.5t31.5 93.5zM768 768q0 80 -56 136t-136 56t-136 -56t-56 -136t56 -136t136 -56t136 56
+t56 136zM1792 288v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1408 544v64q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1792 544v64q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23
+v-64q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1792 800v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM128 1152h1792v96q0 14 -9 23t-23 9h-1728q-14 0 -23 -9t-9 -23v-96zM2048 1248v-1216q0 -66 -47 -113t-113 -47h-1728
+q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1728q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2C3" unicode="&#xf2c3;" horiz-adv-x="2048" 
+d="M896 324q0 -55 -31.5 -93.5t-75.5 -38.5h-426q-44 0 -75.5 38.5t-31.5 93.5q0 54 7.5 100.5t24.5 90t51 68.5t81 25q64 -64 156 -64t156 64q47 0 81 -25t51 -68.5t24.5 -90t7.5 -100.5zM768 768q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136z
+M1792 352v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1408 608v-64q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h320q14 0 23 -9t9 -23zM1792 608v-64q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v64
+q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 864v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1920 32v1120h-1792v-1120q0 -13 9.5 -22.5t22.5 -9.5h1728q13 0 22.5 9.5t9.5 22.5zM2048 1248v-1216q0 -66 -47 -113t-113 -47
+h-1728q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1728q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2C4" unicode="&#xf2c4;" horiz-adv-x="1792" 
+d="M1255 749q0 318 -105 474.5t-330 156.5q-222 0 -326 -157t-104 -474q0 -316 104 -471.5t326 -155.5q74 0 131 17q-22 43 -39 73t-44 65t-53.5 56.5t-63 36t-77.5 14.5q-46 0 -79 -16l-49 97q105 91 276 91q132 0 215.5 -54t150.5 -155q67 149 67 402zM1645 117h117
+q3 -27 -2 -67t-26.5 -95t-58 -100.5t-107 -78t-162.5 -32.5q-71 0 -130.5 19t-105.5 56t-79 78t-66 96q-97 -27 -205 -27q-150 0 -292.5 58t-253 158.5t-178 249t-67.5 317.5q0 170 67.5 319.5t178.5 250.5t253.5 159t291.5 58q121 0 238.5 -36t217 -106t176 -164.5
+t119.5 -219t43 -261.5q0 -190 -80.5 -347.5t-218.5 -264.5q47 -70 93.5 -106.5t104.5 -36.5q61 0 94 37.5t38 85.5z" />
+    <glyph glyph-name="uniF2C5" unicode="&#xf2c5;" horiz-adv-x="2304" 
+d="M453 -101q0 -21 -16 -37.5t-37 -16.5q-1 0 -13 3q-63 15 -162 140q-225 284 -225 676q0 341 213 614q39 51 95 103.5t94 52.5q19 0 35 -13.5t16 -32.5q0 -27 -63 -90q-98 -102 -147 -184q-119 -199 -119 -449q0 -281 123 -491q50 -85 136 -173q2 -3 14.5 -16t19.5 -21
+t17 -20.5t14.5 -23.5t4.5 -21zM1796 33q0 -29 -17.5 -48.5t-46.5 -19.5h-1081q-26 0 -45 19t-19 45q0 29 17.5 48.5t46.5 19.5h1081q26 0 45 -19t19 -45zM1581 644q0 -134 -67 -233q-25 -38 -69.5 -78.5t-83.5 -60.5q-16 -10 -27 -10q-7 0 -15 6t-8 12q0 9 19 30t42 46
+t42 67.5t19 88.5q0 76 -35 130q-29 42 -46 42q-3 0 -3 -5q0 -12 7.5 -35.5t7.5 -36.5q0 -22 -21.5 -35t-44.5 -13q-66 0 -66 76q0 15 1.5 44t1.5 44q0 25 -10 46q-13 25 -42 53.5t-51 28.5q-5 0 -7 -0.5t-3.5 -2.5t-1.5 -6q0 -2 16 -26t16 -54q0 -37 -19 -68t-46 -54
+t-53.5 -46t-45.5 -54t-19 -68q0 -98 42 -160q29 -43 79 -63q16 -5 17 -10q1 -2 1 -5q0 -16 -18 -16q-6 0 -33 11q-119 43 -195 139.5t-76 218.5q0 55 24.5 115.5t60 115t70.5 108.5t59.5 113.5t24.5 111.5q0 53 -25 94q-29 48 -56 64q-19 9 -19 21q0 20 41 20q50 0 110 -29
+q41 -19 71 -44.5t49.5 -51t33.5 -62.5t22 -69t16 -80q0 -1 3 -17.5t4.5 -25t5.5 -25t9 -27t11 -21.5t14.5 -16.5t18.5 -5.5q23 0 37 14t14 37q0 25 -20 67t-20 52t10 10q27 0 93 -70q72 -76 102.5 -156t30.5 -186zM2304 615q0 -274 -138 -503q-19 -32 -48 -72t-68 -86.5
+t-81 -77t-74 -30.5q-16 0 -31 15.5t-15 31.5q0 15 29 50.5t68.5 77t48.5 52.5q183 230 183 531q0 131 -20.5 235t-72.5 211q-58 119 -163 228q-2 3 -13 13.5t-16.5 16.5t-15 17.5t-15 20t-9.5 18.5t-4 19q0 19 16 35.5t35 16.5q70 0 196 -169q98 -131 146 -273t60 -314
+q2 -42 2 -64z" />
+    <glyph glyph-name="uniF2C6" unicode="&#xf2c6;" horiz-adv-x="1792" 
+d="M1189 229l147 693q9 44 -10.5 63t-51.5 7l-864 -333q-29 -11 -39.5 -25t-2.5 -26.5t32 -19.5l221 -69l513 323q21 14 32 6q7 -5 -4 -15l-415 -375v0v0l-16 -228q23 0 45 22l108 104l224 -165q64 -36 81 38zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71
+t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="uniF2C7" unicode="&#xf2c7;" horiz-adv-x="1024" 
+d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v907h128v-907q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5
+t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192
+v128h192z" />
+    <glyph glyph-name="uniF2C8" unicode="&#xf2c8;" horiz-adv-x="1024" 
+d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v651h128v-651q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5
+t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192
+v128h192z" />
+    <glyph glyph-name="uniF2C9" unicode="&#xf2c9;" horiz-adv-x="1024" 
+d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v395h128v-395q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5
+t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192
+v128h192z" />
+    <glyph glyph-name="uniF2CA" unicode="&#xf2ca;" horiz-adv-x="1024" 
+d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 60 35 110t93 71v139h128v-139q58 -21 93 -71t35 -110zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5
+t93.5 226.5zM896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192
+v128h192z" />
+    <glyph glyph-name="uniF2CB" unicode="&#xf2cb;" horiz-adv-x="1024" 
+d="M640 192q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 79 56 135.5t136 56.5t136 -56.5t56 -135.5zM768 192q0 77 -34 144t-94 112v768q0 80 -56 136t-136 56t-136 -56t-56 -136v-768q-60 -45 -94 -112t-34 -144q0 -133 93.5 -226.5t226.5 -93.5t226.5 93.5t93.5 226.5z
+M896 192q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 182 128 313v711q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5v-711q128 -131 128 -313zM1024 768v-128h-192v128h192zM1024 1024v-128h-192v128h192zM1024 1280v-128h-192v128h192z" />
+    <glyph glyph-name="uniF2CC" unicode="&#xf2cc;" horiz-adv-x="1920" 
+d="M1433 1287q10 -10 10 -23t-10 -23l-626 -626q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l44 44q-72 91 -81.5 207t46.5 215q-74 71 -176 71q-106 0 -181 -75t-75 -181v-1280h-256v1280q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5q106 0 201 -41
+t166 -115q94 39 197 24.5t185 -79.5l44 44q10 10 23 10t23 -10zM1344 1024q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1600 896q-26 0 -45 19t-19 45t19 45t45 19t45 -19t19 -45t-19 -45t-45 -19zM1856 1024q26 0 45 -19t19 -45t-19 -45t-45 -19
+t-45 19t-19 45t19 45t45 19zM1216 896q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1408 832q0 26 19 45t45 19t45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45zM1728 896q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1088 768
+q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1344 640q-26 0 -45 19t-19 45t19 45t45 19t45 -19t19 -45t-19 -45t-45 -19zM1600 768q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1216 512q-26 0 -45 19t-19 45t19 45t45 19t45 -19
+t19 -45t-19 -45t-45 -19zM1472 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1088 512q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1344 512q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1216 384
+q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1088 256q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19z" />
+    <glyph glyph-name="uniF2CD" unicode="&#xf2cd;" horiz-adv-x="1792" 
+d="M1664 448v-192q0 -169 -128 -286v-194q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v118q-63 -22 -128 -22h-768q-65 0 -128 22v-110q0 -17 -9.5 -28.5t-22.5 -11.5h-64q-13 0 -22.5 11.5t-9.5 28.5v186q-128 117 -128 286v192h1536zM704 864q0 -14 -9 -23t-23 -9t-23 9
+t-9 23t9 23t23 9t23 -9t9 -23zM768 928q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM704 992q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM832 992q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM768 1056q0 -14 -9 -23t-23 -9t-23 9
+t-9 23t9 23t23 9t23 -9t9 -23zM704 1120q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM1792 608v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v640q0 106 75 181t181 75q108 0 184 -78q46 19 98 12t93 -39l22 22q11 11 22 0l42 -42
+q11 -11 0 -22l-314 -314q-11 -11 -22 0l-42 42q-11 11 0 22l22 22q-36 46 -40.5 104t23.5 108q-37 35 -88 35q-53 0 -90.5 -37.5t-37.5 -90.5v-640h1504q14 0 23 -9t9 -23zM896 1056q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM832 1120q0 -14 -9 -23t-23 -9
+t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM768 1184q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM960 1120q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM896 1184q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM832 1248q0 -14 -9 -23
+t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM1024 1184q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM960 1248q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23zM1088 1248q0 -14 -9 -23t-23 -9t-23 9t-9 23t9 23t23 9t23 -9t9 -23z" />
+    <glyph glyph-name="uniF2CE" unicode="&#xf2ce;" 
+d="M994 344q0 -86 -17 -197q-31 -215 -55 -313q-22 -90 -152 -90t-152 90q-24 98 -55 313q-17 110 -17 197q0 168 224 168t224 -168zM1536 768q0 -240 -134 -434t-350 -280q-8 -3 -15 3t-6 15q7 48 10 66q4 32 6 47q1 9 9 12q159 81 255.5 234t96.5 337q0 180 -91 330.5
+t-247 234.5t-337 74q-124 -7 -237 -61t-193.5 -140.5t-128 -202t-46.5 -240.5q1 -184 99 -336.5t257 -231.5q7 -3 9 -12q3 -21 6 -45q1 -9 5 -32.5t6 -35.5q1 -9 -6.5 -15t-15.5 -2q-148 58 -261 169.5t-173.5 264t-52.5 319.5q7 143 66 273.5t154.5 227t225 157.5t272.5 70
+q164 10 315.5 -46.5t261 -160.5t175 -250.5t65.5 -308.5zM994 800q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5zM1282 768q0 -122 -53.5 -228.5t-146.5 -177.5q-8 -6 -16 -2t-10 14q-6 52 -29 92q-7 10 3 20
+q58 54 91 127t33 155q0 111 -58.5 204t-157.5 141.5t-212 36.5q-133 -15 -229 -113t-109 -231q-10 -92 23.5 -176t98.5 -144q10 -10 3 -20q-24 -41 -29 -93q-2 -9 -10 -13t-16 2q-95 74 -148.5 183t-51.5 234q3 131 69 244t177 181.5t241 74.5q144 7 268 -60t196.5 -187.5
+t72.5 -263.5z" />
+    <glyph glyph-name="uniF2D0" unicode="&#xf2d0;" horiz-adv-x="1792" 
+d="M256 128h1280v768h-1280v-768zM1792 1248v-1216q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2D1" unicode="&#xf2d1;" horiz-adv-x="1792" 
+d="M1792 224v-192q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2D2" unicode="&#xf2d2;" horiz-adv-x="2048" 
+d="M256 0h768v512h-768v-512zM1280 512h512v768h-768v-256h96q66 0 113 -47t47 -113v-352zM2048 1376v-960q0 -66 -47 -113t-113 -47h-608v-352q0 -66 -47 -113t-113 -47h-960q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h608v352q0 66 47 113t113 47h960q66 0 113 -47
+t47 -113z" />
+    <glyph glyph-name="uniF2D3" unicode="&#xf2d3;" horiz-adv-x="1792" 
+d="M1175 215l146 146q10 10 10 23t-10 23l-233 233l233 233q10 10 10 23t-10 23l-146 146q-10 10 -23 10t-23 -10l-233 -233l-233 233q-10 10 -23 10t-23 -10l-146 -146q-10 -10 -10 -23t10 -23l233 -233l-233 -233q-10 -10 -10 -23t10 -23l146 -146q10 -10 23 -10t23 10
+l233 233l233 -233q10 -10 23 -10t23 10zM1792 1248v-1216q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2D4" unicode="&#xf2d4;" horiz-adv-x="1792" 
+d="M1257 425l-146 -146q-10 -10 -23 -10t-23 10l-169 169l-169 -169q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l169 169l-169 169q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l169 -169l169 169q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23
+l-169 -169l169 -169q10 -10 10 -23t-10 -23zM256 128h1280v1024h-1280v-1024zM1792 1248v-1216q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2D5" unicode="&#xf2d5;" horiz-adv-x="1792" 
+d="M1070 358l306 564h-654l-306 -564h654zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="uniF2D6" unicode="&#xf2d6;" horiz-adv-x="1794" 
+d="M1291 1060q-15 17 -35 8.5t-26 -28.5t5 -38q14 -17 40 -14.5t34 20.5t-18 52zM895 814q-8 -8 -19.5 -8t-18.5 8q-8 8 -8 19t8 18q7 8 18.5 8t19.5 -8q7 -7 7 -18t-7 -19zM1060 740l-35 -35q-12 -13 -29.5 -13t-30.5 13l-38 38q-12 13 -12 30t12 30l35 35q12 12 29.5 12
+t30.5 -12l38 -39q12 -12 12 -29.5t-12 -29.5zM951 870q-7 -8 -18.5 -8t-19.5 8q-7 8 -7 19t7 19q8 8 19 8t19 -8t8 -19t-8 -19zM1354 968q-34 -64 -107.5 -85.5t-127.5 16.5q-38 28 -61 66.5t-21 87.5t39 92t75.5 53t70.5 -5t70 -51q2 -2 13 -12.5t14.5 -13.5t13 -13.5
+t12.5 -15.5t10 -15.5t8.5 -18t4 -18.5t1 -21t-5 -22t-9.5 -24zM1555 486q3 20 -8.5 34.5t-27.5 21.5t-33 17t-23 20q-40 71 -84 98.5t-113 11.5q19 13 40 18.5t33 4.5l12 -1q2 45 -34 90q6 20 6.5 40.5t-2.5 30.5l-3 10q43 24 71 65t34 91q10 84 -43 150.5t-137 76.5
+q-60 7 -114 -18.5t-82 -74.5q-30 -51 -33.5 -101t14.5 -87t43.5 -64t56.5 -42q-45 4 -88 36t-57 88q-28 108 32 222q-16 21 -29 32q-50 0 -89 -19q19 24 42 37t36 14l13 1q0 50 -13 78q-10 21 -32.5 28.5t-47 -3.5t-37.5 -40q2 4 4 7q-7 -28 -6.5 -75.5t19 -117t48.5 -122.5
+q-25 -14 -47 -36q-35 -16 -85.5 -70.5t-84.5 -101.5l-33 -46q-90 -34 -181 -125.5t-75 -162.5q1 -16 11 -27q-15 -12 -30 -30q-21 -25 -21 -54t21.5 -40t63.5 6q41 19 77 49.5t55 60.5q-2 2 -6.5 5t-20.5 7.5t-33 3.5q23 5 51 12.5t40 10t27.5 6t26 4t23.5 0.5q14 -7 22 34
+q7 37 7 90q0 102 -40 150q106 -103 101 -219q-1 -29 -15 -50t-27 -27l-13 -6q-4 -7 -19 -32t-26 -45.5t-26.5 -52t-25 -61t-17 -63t-6.5 -66.5t10 -63q-35 54 -37 80q-22 -24 -34.5 -39t-33.5 -42t-30.5 -46t-16.5 -41t-0.5 -38t25.5 -27q45 -25 144 64t190.5 221.5
+t122.5 228.5q86 52 145 115.5t86 119.5q47 -93 154 -178q104 -83 167 -80q39 2 46 43zM1794 640q0 -182 -71 -348t-191 -286t-286.5 -191t-348.5 -71t-348.5 71t-286.5 191t-191 286t-71 348t71 348t191 286t286.5 191t348.5 71t348.5 -71t286.5 -191t191 -286t71 -348z" />
+    <glyph glyph-name="uniF2D7" unicode="&#xf2d7;" 
+d="M518 1353v-655q103 -1 191.5 1.5t125.5 5.5l37 3q68 2 90.5 24.5t39.5 94.5l33 142h103l-14 -322l7 -319h-103l-29 127q-15 68 -45 93t-84 26q-87 8 -352 8v-556q0 -78 43.5 -115.5t133.5 -37.5h357q35 0 59.5 2t55 7.5t54 18t48.5 32t46 50.5t39 73l93 216h89
+q-6 -37 -31.5 -252t-30.5 -276q-146 5 -263.5 8t-162.5 4h-44h-628l-376 -12v102l127 25q67 13 91.5 37t25.5 79l8 643q3 402 -8 645q-2 61 -25.5 84t-91.5 36l-127 24v102l376 -12h702q139 0 374 27q-6 -68 -14 -194.5t-12 -219.5l-5 -92h-93l-32 124q-31 121 -74 179.5
+t-113 58.5h-548q-28 0 -35.5 -8.5t-7.5 -30.5z" />
+    <glyph glyph-name="uniF2D8" unicode="&#xf2d8;" 
+d="M922 739v-182q0 -4 0.5 -15t0 -15l-1.5 -12t-3.5 -11.5t-6.5 -7.5t-11 -5.5t-16 -1.5v309q9 0 16 -1t11 -5t6.5 -5.5t3.5 -9.5t1 -10.5v-13.5v-14zM1238 643v-121q0 -1 0.5 -12.5t0 -15.5t-2.5 -11.5t-7.5 -10.5t-13.5 -3q-9 0 -14 9q-4 10 -4 165v7v8.5v9t1.5 8.5l3.5 7
+t5 5.5t8 1.5q6 0 10 -1.5t6.5 -4.5t4 -6t2 -8.5t0.5 -8v-9.5v-9zM180 407h122v472h-122v-472zM614 407h106v472h-159l-28 -221q-20 148 -32 221h-158v-472h107v312l45 -312h76l43 319v-319zM1039 712q0 67 -5 90q-3 16 -11 28.5t-17 20.5t-25 14t-26.5 8.5t-31 4t-29 1.5
+h-29.5h-12h-91v-472h56q169 -1 197 24.5t25 180.5q-1 62 -1 100zM1356 515v133q0 29 -2 45t-9.5 33.5t-24.5 25t-46 7.5q-46 0 -77 -34v154h-117v-472h110l7 30q30 -36 77 -36q50 0 66 30.5t16 83.5zM1536 1248v-1216q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113
+v1216q0 66 47 113t113 47h1216q66 0 113 -47t47 -113z" />
+    <glyph glyph-name="uniF2D9" unicode="&#xf2d9;" horiz-adv-x="2176" 
+d="M1143 -197q-6 1 -11 4q-13 8 -36 23t-86 65t-116.5 104.5t-112 140t-89.5 172.5q-17 3 -175 37q66 -213 235 -362t391 -184zM502 409l168 -28q-25 76 -41 167.5t-19 145.5l-4 53q-84 -82 -121 -224q5 -65 17 -114zM612 1018q-43 -64 -77 -148q44 46 74 68zM2049 584
+q0 161 -62 307t-167.5 252t-250.5 168.5t-304 62.5q-147 0 -281 -52.5t-240 -148.5q-30 -58 -45 -160q60 51 143 83.5t158.5 43t143 13.5t108.5 -1l40 -3q33 -1 53 -15.5t24.5 -33t6.5 -37t-1 -28.5q-126 11 -227.5 0.5t-183 -43.5t-142.5 -71.5t-131 -98.5
+q4 -36 11.5 -92.5t35.5 -178t62 -179.5q123 -6 247.5 14.5t214.5 53.5t162.5 67t109.5 59l37 24q22 16 39.5 20.5t30.5 -5t17 -34.5q14 -97 -39 -121q-208 -97 -467 -134q-135 -20 -317 -16q41 -96 110 -176.5t137 -127t130.5 -79t101.5 -43.5l39 -12q143 -23 263 15
+q195 99 314 289t119 418zM2123 621q-14 -135 -40 -212q-70 -208 -181.5 -346.5t-318.5 -253.5q-48 -33 -82 -44q-72 -26 -163 -16q-36 -3 -73 -3q-283 0 -504.5 173t-295.5 442q-1 0 -4 0.5t-5 0.5q-6 -50 2.5 -112.5t26 -115t36 -98t31.5 -71.5l14 -26q8 -12 54 -82
+q-71 38 -124.5 106.5t-78.5 140t-39.5 137t-17.5 107.5l-2 42q-5 2 -33.5 12.5t-48.5 18t-53 20.5t-57.5 25t-50 25.5t-42.5 27t-25 25.5q19 -10 50.5 -25.5t113 -45.5t145.5 -38l2 32q11 149 94 290q41 202 176 365q28 115 81 214q15 28 32 45t49 32q158 74 303.5 104
+t302 11t306.5 -97q220 -115 333 -336t87 -474z" />
+    <glyph glyph-name="uniF2DA" unicode="&#xf2da;" horiz-adv-x="1792" 
+d="M1341 752q29 44 -6.5 129.5t-121.5 142.5q-58 39 -125.5 53.5t-118 4.5t-68.5 -37q-12 -23 -4.5 -28t42.5 -10q23 -3 38.5 -5t44.5 -9.5t56 -17.5q36 -13 67.5 -31.5t53 -37t40 -38.5t30.5 -38t22 -34.5t16.5 -28.5t12 -18.5t10.5 -6t11 9.5zM1704 178
+q-52 -127 -148.5 -220t-214.5 -141.5t-253 -60.5t-266 13.5t-251 91t-210 161.5t-141.5 235.5t-46.5 303.5q1 41 8.5 84.5t12.5 64t24 80.5t23 73q-51 -208 1 -397t173 -318t291 -206t346 -83t349 74.5t289 244.5q20 27 18 14q0 -4 -4 -14zM1465 627q0 -104 -40.5 -199
+t-108.5 -164t-162 -109.5t-198 -40.5t-198 40.5t-162 109.5t-108.5 164t-40.5 199t40.5 199t108.5 164t162 109.5t198 40.5t198 -40.5t162 -109.5t108.5 -164t40.5 -199zM1752 915q-65 147 -180.5 251t-253 153.5t-292 53.5t-301 -36.5t-275.5 -129t-220 -211.5t-131 -297
+t-10 -373q-49 161 -51.5 311.5t35.5 272.5t109 227t165.5 180.5t207 126t232 71t242.5 9t236 -54t216 -124.5t178 -197q33 -50 62 -121t31 -112zM1690 573q12 244 -136.5 416t-396.5 240q-8 0 -10 5t24 8q125 -4 230 -50t173 -120t116 -168.5t58.5 -199t-1 -208
+t-61.5 -197.5t-122.5 -167t-185 -117.5t-248.5 -46.5q108 30 201.5 80t174 123t129.5 176.5t55 225.5z" />
+    <glyph glyph-name="uniF2DB" unicode="&#xf2db;" 
+d="M192 256v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM192 512v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM192 768v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16
+q0 16 16 16h112zM192 1024v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM192 1280v-128h-112q-16 0 -16 16v16h-48q-16 0 -16 16v32q0 16 16 16h48v16q0 16 16 16h112zM1280 1440v-1472q0 -40 -28 -68t-68 -28h-832q-40 0 -68 28
+t-28 68v1472q0 40 28 68t68 28h832q40 0 68 -28t28 -68zM1536 208v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 464v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 720v-32
+q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 976v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16h48q16 0 16 -16zM1536 1232v-32q0 -16 -16 -16h-48v-16q0 -16 -16 -16h-112v128h112q16 0 16 -16v-16
+h48q16 0 16 -16z" />
+    <glyph glyph-name="uniF2DC" unicode="&#xf2dc;" horiz-adv-x="1664" 
+d="M1566 419l-167 -33l186 -107q23 -13 29.5 -38.5t-6.5 -48.5q-14 -23 -39 -29.5t-48 6.5l-186 106l55 -160q13 -38 -12 -63.5t-60.5 -20.5t-48.5 42l-102 300l-271 156v-313l208 -238q16 -18 17 -39t-11 -36.5t-28.5 -25t-37 -5.5t-36.5 22l-112 128v-214q0 -26 -19 -45
+t-45 -19t-45 19t-19 45v214l-112 -128q-16 -18 -36.5 -22t-37 5.5t-28.5 25t-11 36.5t17 39l208 238v313l-271 -156l-102 -300q-13 -37 -48.5 -42t-60.5 20.5t-12 63.5l55 160l-186 -106q-23 -13 -48 -6.5t-39 29.5q-13 23 -6.5 48.5t29.5 38.5l186 107l-167 33
+q-29 6 -42 29t-8.5 46.5t25.5 40t50 10.5l310 -62l271 157l-271 157l-310 -62q-4 -1 -13 -1q-27 0 -44 18t-19 40t11 43t40 26l167 33l-186 107q-23 13 -29.5 38.5t6.5 48.5t39 30t48 -7l186 -106l-55 160q-13 38 12 63.5t60.5 20.5t48.5 -42l102 -300l271 -156v313
+l-208 238q-16 18 -17 39t11 36.5t28.5 25t37 5.5t36.5 -22l112 -128v214q0 26 19 45t45 19t45 -19t19 -45v-214l112 128q16 18 36.5 22t37 -5.5t28.5 -25t11 -36.5t-17 -39l-208 -238v-313l271 156l102 300q13 37 48.5 42t60.5 -20.5t12 -63.5l-55 -160l186 106
+q23 13 48 6.5t39 -29.5q13 -23 6.5 -48.5t-29.5 -38.5l-186 -107l167 -33q27 -5 40 -26t11 -43t-19 -40t-44 -18q-9 0 -13 1l-310 62l-271 -157l271 -157l310 62q29 6 50 -10.5t25.5 -40t-8.5 -46.5t-42 -29z" />
+    <glyph glyph-name="uniF2DD" unicode="&#xf2dd;" horiz-adv-x="1792" 
+d="M1473 607q7 118 -33 226.5t-113 189t-177 131t-221 57.5q-116 7 -225.5 -32t-192 -110.5t-135 -175t-59.5 -220.5q-7 -118 33 -226.5t113 -189t177.5 -131t221.5 -57.5q155 -9 293 59t224 195.5t94 283.5zM1792 1536l-349 -348q120 -117 180.5 -272t50.5 -321
+q-11 -183 -102 -339t-241 -255.5t-332 -124.5l-999 -132l347 347q-120 116 -180.5 271.5t-50.5 321.5q11 184 102 340t241.5 255.5t332.5 124.5q167 22 500 66t500 66z" />
+    <glyph glyph-name="uniF2DE" unicode="&#xf2de;" horiz-adv-x="1792" 
+d="M948 508l163 -329h-51l-175 350l-171 -350h-49l179 374l-78 33l21 49l240 -102l-21 -50zM563 1100l304 -130l-130 -304l-304 130zM907 915l240 -103l-103 -239l-239 102zM1188 765l191 -81l-82 -190l-190 81zM1680 640q0 159 -62 304t-167.5 250.5t-250.5 167.5t-304 62
+t-304 -62t-250.5 -167.5t-167.5 -250.5t-62 -304t62 -304t167.5 -250.5t250.5 -167.5t304 -62t304 62t250.5 167.5t167.5 250.5t62 304zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71
+t286 -191t191 -286t71 -348z" />
+    <glyph glyph-name="uniF2E0" unicode="&#xf2e0;" horiz-adv-x="1920" 
+d="M1334 302q-4 24 -27.5 34t-49.5 10.5t-48.5 12.5t-25.5 38q-5 47 33 139.5t75 181t32 127.5q-14 101 -117 103q-45 1 -75 -16l-3 -2l-5 -2.5t-4.5 -2t-5 -2t-5 -0.5t-6 1.5t-6 3.5t-6.5 5q-3 2 -9 8.5t-9 9t-8.5 7.5t-9.5 7.5t-9.5 5.5t-11 4.5t-11.5 2.5q-30 5 -48 -3
+t-45 -31q-1 -1 -9 -8.5t-12.5 -11t-15 -10t-16.5 -5.5t-17 3q-54 27 -84 40q-41 18 -94 -5t-76 -65q-16 -28 -41 -98.5t-43.5 -132.5t-40 -134t-21.5 -73q-22 -69 18.5 -119t110.5 -46q30 2 50.5 15t38.5 46q7 13 79 199.5t77 194.5q6 11 21.5 18t29.5 0q27 -15 21 -53
+q-2 -18 -51 -139.5t-50 -132.5q-6 -38 19.5 -56.5t60.5 -7t55 49.5q4 8 45.5 92t81.5 163.5t46 88.5q20 29 41 28q29 0 25 -38q-2 -16 -65.5 -147.5t-70.5 -159.5q-12 -53 13 -103t74 -74q17 -9 51 -15.5t71.5 -8t62.5 14t20 48.5zM383 86q3 -15 -5 -27.5t-23 -15.5
+q-14 -3 -26.5 5t-15.5 23q-3 14 5 27t22 16t27 -5t16 -23zM953 -177q12 -17 8.5 -37.5t-20.5 -32.5t-37.5 -8t-32.5 21q-11 17 -7.5 37.5t20.5 32.5t37.5 8t31.5 -21zM177 635q-18 -27 -49.5 -33t-57.5 13q-26 18 -32 50t12 58q18 27 49.5 33t57.5 -12q26 -19 32 -50.5
+t-12 -58.5zM1467 -42q19 -28 13 -61.5t-34 -52.5t-60.5 -13t-51.5 34t-13 61t33 53q28 19 60.5 13t52.5 -34zM1579 562q69 -113 42.5 -244.5t-134.5 -207.5q-90 -63 -199 -60q-20 -80 -84.5 -127t-143.5 -44.5t-140 57.5q-12 -9 -13 -10q-103 -71 -225 -48.5t-193 126.5
+q-50 73 -53 164q-83 14 -142.5 70.5t-80.5 128t-2 152t81 138.5q-36 60 -38 128t24.5 125t79.5 98.5t121 50.5q32 85 99 148t146.5 91.5t168 17t159.5 -66.5q72 21 140 17.5t128.5 -36t104.5 -80t67.5 -115t17.5 -140.5q52 -16 87 -57t45.5 -89t-5.5 -99.5t-58 -87.5z
+M455 1222q14 -20 9.5 -44.5t-24.5 -38.5q-19 -14 -43.5 -9.5t-37.5 24.5q-14 20 -9.5 44.5t24.5 38.5q19 14 43.5 9.5t37.5 -24.5zM614 1503q4 -16 -5 -30.5t-26 -18.5t-31 5.5t-18 26.5q-3 17 6.5 31t25.5 18q17 4 31 -5.5t17 -26.5zM1800 555q4 -20 -6.5 -37t-30.5 -21
+q-19 -4 -36 6.5t-21 30.5t6.5 37t30.5 22q20 4 36.5 -7.5t20.5 -30.5zM1136 1448q16 -27 8.5 -58.5t-35.5 -47.5q-27 -16 -57.5 -8.5t-46.5 34.5q-16 28 -8.5 59t34.5 48t58 9t47 -36zM1882 792q4 -15 -4 -27.5t-23 -16.5q-15 -3 -27.5 5.5t-15.5 22.5q-3 15 5 28t23 16
+q14 3 26.5 -5t15.5 -23zM1691 1033q15 -22 10.5 -49t-26.5 -43q-22 -15 -49 -10t-42 27t-10 49t27 43t48.5 11t41.5 -28z" />
+    <glyph glyph-name="uniF2E1" unicode="&#xf2e1;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E2" unicode="&#xf2e2;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E3" unicode="&#xf2e3;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E4" unicode="&#xf2e4;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E5" unicode="&#xf2e5;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E6" unicode="&#xf2e6;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E7" unicode="&#xf2e7;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="_698" unicode="&#xf2e8;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2E9" unicode="&#xf2e9;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2EA" unicode="&#xf2ea;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2EB" unicode="&#xf2eb;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2EC" unicode="&#xf2ec;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2ED" unicode="&#xf2ed;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="uniF2EE" unicode="&#xf2ee;" horiz-adv-x="1792" 
+ />
+    <glyph glyph-name="lessequal" unicode="&#xf500;" horiz-adv-x="1792" 
+ />
+  </font>
+</defs></svg>
diff --git a/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.ttf b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.ttf
new file mode 100644
Binary files /dev/null and b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.ttf differ
diff --git a/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.woff b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.woff
new file mode 100644
Binary files /dev/null and b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.woff differ
diff --git a/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.woff2 b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.woff2
new file mode 100644
Binary files /dev/null and b/test/fixtures/cache-tests/asset/fonts/fontawesome-webfont.woff2 differ
diff --git a/test/fixtures/cache-tests/asset/liquid.browser.esm.mjs b/test/fixtures/cache-tests/asset/liquid.browser.esm.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/asset/liquid.browser.esm.mjs
@@ -0,0 +1,2799 @@
+/*
+ * liquidjs@9.16.0, https://github.com/harttle/liquidjs
+ * (c) 2016-2020 harttle
+ * Released under the MIT License.
+ */
+class Drop {
+  valueOf () {
+    return undefined
+  }
+
+  liquidMethodMissing (key) {
+    return undefined
+  }
+}
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+var __assign = function () {
+  __assign = Object.assign || function __assign (t) {
+    for (var s, i = 1, n = arguments.length; i < n; i++) {
+      s = arguments[i]
+      for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]
+    }
+    return t
+  }
+  return __assign.apply(this, arguments)
+}
+
+const toStr = Object.prototype.toString
+const toLowerCase = String.prototype.toLowerCase
+/*
+ * Checks if value is classified as a String primitive or object.
+ * @param {any} value The value to check.
+ * @return {Boolean} Returns true if value is a string, else false.
+ */
+function isString (value) {
+  return toStr.call(value) === '[object String]'
+}
+function isFunction (value) {
+  return typeof value === 'function'
+}
+function stringify (value) {
+  value = toValue(value)
+  return isNil(value) ? '' : String(value)
+}
+function toValue (value) {
+  return value instanceof Drop ? value.valueOf() : value
+}
+function isNumber (value) {
+  return typeof value === 'number'
+}
+function toLiquid (value) {
+  if (value && isFunction(value.toLiquid)) { return toLiquid(value.toLiquid()) }
+  return value
+}
+function isNil (value) {
+  return value === null || value === undefined
+}
+function isArray (value) {
+  // be compatible with IE 8
+  return toStr.call(value) === '[object Array]'
+}
+/*
+ * Iterates over own enumerable string keyed properties of an object and invokes iteratee for each property.
+ * The iteratee is invoked with three arguments: (value, key, object).
+ * Iteratee functions may exit iteration early by explicitly returning false.
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @return {Object} Returns object.
+ */
+function forOwn (object, iteratee) {
+  object = object || {}
+  for (const k in object) {
+    if (object.hasOwnProperty(k)) {
+      if (iteratee(object[k], k, object) === false) { break }
+    }
+  }
+  return object
+}
+function last (arr) {
+  return arr[arr.length - 1]
+}
+/*
+ * Checks if value is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))
+ * @param {any} value The value to check.
+ * @return {Boolean} Returns true if value is an object, else false.
+ */
+function isObject (value) {
+  const type = typeof value
+  return value !== null && (type === 'object' || type === 'function')
+}
+function range (start, stop, step = 1) {
+  const arr = []
+  for (let i = start; i < stop; i += step) {
+    arr.push(i)
+  }
+  return arr
+}
+function padStart (str, length, ch = ' ') {
+  return pad(str, length, ch, (str, ch) => ch + str)
+}
+function padEnd (str, length, ch = ' ') {
+  return pad(str, length, ch, (str, ch) => str + ch)
+}
+function pad (str, length, ch, add) {
+  str = String(str)
+  let n = length - str.length
+  while (n-- > 0) { str = add(str, ch) }
+  return str
+}
+function identify (val) {
+  return val
+}
+function snakeCase (str) {
+  return str.replace(/(\w?)([A-Z])/g, (_, a, b) => (a ? a + '_' : '') + b.toLowerCase())
+}
+function changeCase (str) {
+  const hasLowerCase = [...str].some(ch => ch >= 'a' && ch <= 'z')
+  return hasLowerCase ? str.toUpperCase() : str.toLowerCase()
+}
+function ellipsis (str, N) {
+  return str.length > N ? str.substr(0, N - 3) + '...' : str
+}
+// compare string in case-insensitive way, undefined values to the tail
+function caseInsensitiveCompare (a, b) {
+  if (a == null && b == null) { return 0 }
+  if (a == null) { return 1 }
+  if (b == null) { return -1 }
+  a = toLowerCase.call(a)
+  b = toLowerCase.call(b)
+  if (a < b) { return -1 }
+  if (a > b) { return 1 }
+  return 0
+}
+
+class Node {
+  constructor (key, value, next, prev) {
+    this.key = key
+    this.value = value
+    this.next = next
+    this.prev = prev
+  }
+}
+class LRU {
+  constructor (limit, size = 0) {
+    this.limit = limit
+    this.size = size
+    this.cache = {}
+    this.head = new Node('HEAD', null, null, null)
+    this.tail = new Node('TAIL', null, null, null)
+    this.head.next = this.tail
+    this.tail.prev = this.head
+  }
+
+  write (key, value) {
+    if (this.cache[key]) {
+      this.cache[key].value = value
+    } else {
+      const node = new Node(key, value, this.head.next, this.head)
+      this.head.next.prev = node
+      this.head.next = node
+      this.cache[key] = node
+      this.size++
+      this.ensureLimit()
+    }
+  }
+
+  read (key) {
+    if (!this.cache[key]) { return }
+    const { value } = this.cache[key]
+    this.remove(key)
+    this.write(key, value)
+    return value
+  }
+
+  remove (key) {
+    const node = this.cache[key]
+    node.prev.next = node.next
+    node.next.prev = node.prev
+    delete this.cache[key]
+    this.size--
+  }
+
+  clear () {
+    this.head.next = this.tail
+    this.tail.prev = this.head
+    this.size = 0
+    this.cache = {}
+  }
+
+  ensureLimit () {
+    if (this.size > this.limit) { this.remove(this.tail.prev.key) }
+  }
+}
+
+const defaultOptions = {
+  root: ['.'],
+  cache: undefined,
+  extname: '',
+  dynamicPartials: true,
+  jsTruthy: false,
+  trimTagRight: false,
+  trimTagLeft: false,
+  trimOutputRight: false,
+  trimOutputLeft: false,
+  greedy: true,
+  tagDelimiterLeft: '{%',
+  tagDelimiterRight: '%}',
+  outputDelimiterLeft: '{{',
+  outputDelimiterRight: '}}',
+  strictFilters: false,
+  strictVariables: false,
+  globals: {}
+}
+function normalize (options) {
+  options = options || {}
+  if (options.hasOwnProperty('root')) {
+    options.root = normalizeStringArray(options.root)
+  }
+  if (options.hasOwnProperty('cache')) {
+    let cache
+    if (typeof options.cache === 'number') { cache = options.cache > 0 ? new LRU(options.cache) : undefined } else if (typeof options.cache === 'object') { cache = options.cache } else { cache = options.cache ? new LRU(1024) : undefined }
+    options.cache = cache
+  }
+  return options
+}
+function applyDefault (options) {
+  return Object.assign({}, defaultOptions, options)
+}
+function normalizeStringArray (value) {
+  if (isArray(value)) { return value }
+  if (isString(value)) { return [value] }
+  return []
+}
+
+class Context {
+  constructor (env = {}, opts = defaultOptions, sync = false) {
+    this.scopes = [{}]
+    this.registers = {}
+    this.sync = sync
+    this.opts = opts
+    this.globals = opts.globals
+    this.environments = env
+  }
+
+  getRegister (key, defaultValue = {}) {
+    return (this.registers[key] = this.registers[key] || defaultValue)
+  }
+
+  setRegister (key, value) {
+    return (this.registers[key] = value)
+  }
+
+  saveRegister (...keys) {
+    return keys.map(key => [key, this.getRegister(key)])
+  }
+
+  restoreRegister (keyValues) {
+    return keyValues.forEach(([key, value]) => this.setRegister(key, value))
+  }
+
+  getAll () {
+    return [this.globals, this.environments, ...this.scopes]
+      .reduce((ctx, val) => __assign(ctx, val), {})
+  }
+
+  get (paths) {
+    const scope = this.findScope(paths[0])
+    return this.getFromScope(scope, paths)
+  }
+
+  getFromScope (scope, paths) {
+    if (typeof paths === 'string') { paths = paths.split('.') }
+    return paths.reduce((scope, path) => {
+      scope = readProperty(scope, path)
+      if (isNil(scope) && this.opts.strictVariables) {
+        throw new TypeError(`undefined variable: ${path}`)
+      }
+      return scope
+    }, scope)
+  }
+
+  push (ctx) {
+    return this.scopes.push(ctx)
+  }
+
+  pop () {
+    return this.scopes.pop()
+  }
+
+  bottom () {
+    return this.scopes[0]
+  }
+
+  findScope (key) {
+    for (let i = this.scopes.length - 1; i >= 0; i--) {
+      const candidate = this.scopes[i]
+      if (key in candidate) { return candidate }
+    }
+    if (key in this.environments) { return this.environments }
+    return this.globals
+  }
+}
+function readProperty (obj, key) {
+  if (isNil(obj)) { return obj }
+  obj = toLiquid(obj)
+  if (obj instanceof Drop) {
+    if (isFunction(obj[key])) { return obj[key]() }
+    if (obj.hasOwnProperty(key)) { return obj[key] }
+    return obj.liquidMethodMissing(key)
+  }
+  if (key === 'size') { return readSize(obj) }
+  if (key === 'first') { return readFirst(obj) }
+  if (key === 'last') { return readLast(obj) }
+  return obj[key]
+}
+function readFirst (obj) {
+  if (isArray(obj)) { return obj[0] }
+  return obj.first
+}
+function readLast (obj) {
+  if (isArray(obj)) { return obj[obj.length - 1] }
+  return obj.last
+}
+function readSize (obj) {
+  if (isArray(obj) || isString(obj)) { return obj.length }
+  return obj.size
+}
+
+function domResolve (root, path) {
+  const base = document.createElement('base')
+  base.href = root
+  const head = document.getElementsByTagName('head')[0]
+  head.insertBefore(base, head.firstChild)
+  const a = document.createElement('a')
+  a.href = path
+  const resolved = a.href
+  head.removeChild(base)
+  return resolved
+}
+function resolve (root, filepath, ext) {
+  if (root.length && last(root) !== '/') { root += '/' }
+  const url = domResolve(root, filepath)
+  return url.replace(/^(\w+:\/\/[^/]+)(\/[^?]+)/, (str, origin, path) => {
+    const last = path.split('/').pop()
+    if (/\.\w+$/.test(last)) { return str }
+    return origin + path + ext
+  })
+}
+async function readFile (url) {
+  return new Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest()
+    xhr.onload = () => {
+      if (xhr.status >= 200 && xhr.status < 300) {
+        resolve(xhr.responseText)
+      } else {
+        reject(new Error(xhr.statusText))
+      }
+    }
+    xhr.onerror = () => {
+      reject(new Error('An error occurred whilst receiving the response.'))
+    }
+    xhr.open('GET', url)
+    xhr.send()
+  })
+}
+function readFileSync (url) {
+  const xhr = new XMLHttpRequest()
+  xhr.open('GET', url, false)
+  xhr.send()
+  if (xhr.status < 200 || xhr.status >= 300) {
+    throw new Error(xhr.statusText)
+  }
+  return xhr.responseText
+}
+async function exists (filepath) {
+  return true
+}
+function existsSync (filepath) {
+  return true
+}
+
+var fs = /* #__PURE__ */Object.freeze({
+  resolve: resolve,
+  readFile: readFile,
+  readFileSync: readFileSync,
+  exists: exists,
+  existsSync: existsSync
+})
+
+var TokenKind;
+(function (TokenKind) {
+  TokenKind[TokenKind.Number = 1] = 'Number'
+  TokenKind[TokenKind.Literal = 2] = 'Literal'
+  TokenKind[TokenKind.Tag = 4] = 'Tag'
+  TokenKind[TokenKind.Output = 8] = 'Output'
+  TokenKind[TokenKind.HTML = 16] = 'HTML'
+  TokenKind[TokenKind.Filter = 32] = 'Filter'
+  TokenKind[TokenKind.Hash = 64] = 'Hash'
+  TokenKind[TokenKind.PropertyAccess = 128] = 'PropertyAccess'
+  TokenKind[TokenKind.Word = 256] = 'Word'
+  TokenKind[TokenKind.Range = 512] = 'Range'
+  TokenKind[TokenKind.Quoted = 1024] = 'Quoted'
+  TokenKind[TokenKind.Operator = 2048] = 'Operator'
+  TokenKind[TokenKind.Delimited = 12] = 'Delimited'
+})(TokenKind || (TokenKind = {}))
+
+function isDelimitedToken (val) {
+  return !!(getKind(val) & TokenKind.Delimited)
+}
+function isOperatorToken (val) {
+  return getKind(val) === TokenKind.Operator
+}
+function isHTMLToken (val) {
+  return getKind(val) === TokenKind.HTML
+}
+function isOutputToken (val) {
+  return getKind(val) === TokenKind.Output
+}
+function isTagToken (val) {
+  return getKind(val) === TokenKind.Tag
+}
+function isQuotedToken (val) {
+  return getKind(val) === TokenKind.Quoted
+}
+function isLiteralToken (val) {
+  return getKind(val) === TokenKind.Literal
+}
+function isNumberToken (val) {
+  return getKind(val) === TokenKind.Number
+}
+function isPropertyAccessToken (val) {
+  return getKind(val) === TokenKind.PropertyAccess
+}
+function isWordToken (val) {
+  return getKind(val) === TokenKind.Word
+}
+function isRangeToken (val) {
+  return getKind(val) === TokenKind.Range
+}
+function getKind (val) {
+  return val ? val.kind : -1
+}
+
+var typeGuards = /* #__PURE__ */Object.freeze({
+  isDelimitedToken: isDelimitedToken,
+  isOperatorToken: isOperatorToken,
+  isHTMLToken: isHTMLToken,
+  isOutputToken: isOutputToken,
+  isTagToken: isTagToken,
+  isQuotedToken: isQuotedToken,
+  isLiteralToken: isLiteralToken,
+  isNumberToken: isNumberToken,
+  isPropertyAccessToken: isPropertyAccessToken,
+  isWordToken: isWordToken,
+  isRangeToken: isRangeToken
+})
+
+// **DO NOT CHANGE THIS FILE**
+//
+// This file is generated by bin/character-gen.js
+// bitmask character types to boost performance
+const TYPES = [0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 4, 4, 4, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 2, 8, 0, 0, 0, 0, 8, 0, 0, 0, 64, 0, 65, 0, 0, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 2, 2, 2, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
+const VARIABLE = 1
+const BLANK = 4
+const QUOTE = 8
+const INLINE_BLANK = 16
+const NUMBER = 32
+const SIGN = 64
+TYPES[160] = TYPES[5760] = TYPES[6158] = TYPES[8192] = TYPES[8193] = TYPES[8194] = TYPES[8195] = TYPES[8196] = TYPES[8197] = TYPES[8198] = TYPES[8199] = TYPES[8200] = TYPES[8201] = TYPES[8202] = TYPES[8232] = TYPES[8233] = TYPES[8239] = TYPES[8287] = TYPES[12288] = BLANK
+
+function whiteSpaceCtrl (tokens, options) {
+  options = Object.assign({ greedy: true }, options)
+  let inRaw = false
+  for (let i = 0; i < tokens.length; i++) {
+    const token = tokens[i]
+    if (!isDelimitedToken(token)) { continue }
+    if (!inRaw && token.trimLeft) {
+      trimLeft(tokens[i - 1], options.greedy)
+    }
+    if (isTagToken(token)) {
+      if (token.name === 'raw') { inRaw = true } else if (token.name === 'endraw') { inRaw = false }
+    }
+    if (!inRaw && token.trimRight) {
+      trimRight(tokens[i + 1], options.greedy)
+    }
+  }
+}
+function trimLeft (token, greedy) {
+  if (!token || !isHTMLToken(token)) { return }
+  const mask = greedy ? BLANK : INLINE_BLANK
+  while (TYPES[token.input.charCodeAt(token.end - 1 - token.trimRight)] & mask) { token.trimRight++ }
+}
+function trimRight (token, greedy) {
+  if (!token || !isHTMLToken(token)) { return }
+  const mask = greedy ? BLANK : INLINE_BLANK
+  while (TYPES[token.input.charCodeAt(token.begin + token.trimLeft)] & mask) { token.trimLeft++ }
+  if (token.input.charAt(token.begin + token.trimLeft) === '\n') { token.trimLeft++ }
+}
+
+class Token {
+  constructor (kind, input, begin, end, file) {
+    this.kind = kind
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.file = file
+  }
+
+  getText () {
+    return this.input.slice(this.begin, this.end)
+  }
+
+  getPosition () {
+    let [row, col] = [1, 1]
+    for (let i = 0; i < this.begin; i++) {
+      if (this.input[i] === '\n') {
+        row++
+        col = 1
+      } else { col++ }
+    }
+    return [row, col]
+  }
+
+  size () {
+    return this.end - this.begin
+  }
+}
+
+class NumberToken extends Token {
+  constructor (whole, decimal) {
+    super(TokenKind.Number, whole.input, whole.begin, decimal ? decimal.end : whole.end, whole.file)
+    this.whole = whole
+    this.decimal = decimal
+  }
+}
+
+// a word can be an identifier, a number, a keyword or a single-word-literal
+class WordToken extends Token {
+  constructor (input, begin, end, file) {
+    super(TokenKind.Word, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.file = file
+    this.content = this.getText()
+  }
+
+  isNumber (allowSign = false) {
+    const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
+      ? this.begin + 1
+      : this.begin
+    for (let i = begin; i < this.end; i++) {
+      if (!(TYPES[this.input.charCodeAt(i)] & NUMBER)) { return false }
+    }
+    return true
+  }
+}
+
+class EmptyDrop extends Drop {
+  equals (value) {
+    if (isString(value) || isArray(value)) { return value.length === 0 }
+    if (isObject(value)) { return Object.keys(value).length === 0 }
+    return false
+  }
+
+  gt () {
+    return false
+  }
+
+  geq () {
+    return false
+  }
+
+  lt () {
+    return false
+  }
+
+  leq () {
+    return false
+  }
+
+  valueOf () {
+    return ''
+  }
+}
+
+class BlankDrop extends EmptyDrop {
+  equals (value) {
+    if (value === false) { return true }
+    if (isNil(toValue(value))) { return true }
+    if (isString(value)) { return /^\s*$/.test(value) }
+    return super.equals(value)
+  }
+}
+
+class NullDrop extends Drop {
+  equals (value) {
+    return isNil(toValue(value)) || value instanceof BlankDrop
+  }
+
+  gt () {
+    return false
+  }
+
+  geq () {
+    return false
+  }
+
+  lt () {
+    return false
+  }
+
+  leq () {
+    return false
+  }
+
+  valueOf () {
+    return null
+  }
+}
+
+const literalValues = {
+  true: true,
+  false: false,
+  nil: new NullDrop(),
+  null: new NullDrop(),
+  empty: new EmptyDrop(),
+  blank: new BlankDrop()
+}
+
+class LiteralToken extends Token {
+  constructor (input, begin, end, file) {
+    super(TokenKind.Literal, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.file = file
+    this.literal = this.getText()
+  }
+}
+
+const precedence = {
+  '==': 1,
+  '!=': 1,
+  '>': 1,
+  '<': 1,
+  '>=': 1,
+  '<=': 1,
+  contains: 1,
+  and: 0,
+  or: 0
+}
+class OperatorToken extends Token {
+  constructor (input, begin, end, file) {
+    super(TokenKind.Operator, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.file = file
+    this.operator = this.getText()
+  }
+
+  getPrecedence () {
+    return precedence[this.getText()]
+  }
+}
+
+const rHex = /[\da-fA-F]/
+const rOct = /[0-7]/
+const escapeChar = {
+  b: '\b',
+  f: '\f',
+  n: '\n',
+  r: '\r',
+  t: '\t',
+  v: '\x0B'
+}
+function hexVal (c) {
+  const code = c.charCodeAt(0)
+  if (code >= 97) { return code - 87 }
+  if (code >= 65) { return code - 55 }
+  return code - 48
+}
+function parseStringLiteral (str) {
+  let ret = ''
+  for (let i = 1; i < str.length - 1; i++) {
+    if (str[i] !== '\\') {
+      ret += str[i]
+      continue
+    }
+    if (escapeChar[str[i + 1]] !== undefined) {
+      ret += escapeChar[str[++i]]
+    } else if (str[i + 1] === 'u') {
+      let val = 0
+      let j = i + 2
+      while (j <= i + 5 && rHex.test(str[j])) {
+        val = val * 16 + hexVal(str[j++])
+      }
+      i = j - 1
+      ret += String.fromCharCode(val)
+    } else if (!rOct.test(str[i + 1])) {
+      ret += str[++i]
+    } else {
+      let j = i + 1
+      let val = 0
+      while (j <= i + 3 && rOct.test(str[j])) {
+        val = val * 8 + hexVal(str[j++])
+      }
+      i = j - 1
+      ret += String.fromCharCode(val)
+    }
+  }
+  return ret
+}
+
+class PropertyAccessToken extends Token {
+  constructor (variable, props, end) {
+    super(TokenKind.PropertyAccess, variable.input, variable.begin, end, variable.file)
+    this.variable = variable
+    this.props = props
+  }
+
+  getVariableAsText () {
+    if (this.variable instanceof WordToken) {
+      return this.variable.getText()
+    } else {
+      return parseStringLiteral(this.variable.getText())
+    }
+  }
+}
+
+class LiquidError extends Error {
+  constructor (err, token) {
+    super(err.message)
+    this.originalError = err
+    this.token = token
+  }
+
+  update () {
+    const err = this.originalError
+    const context = mkContext(this.token)
+    this.message = mkMessage(err.message, this.token)
+    this.stack = this.message + '\n' + context +
+            '\n' + this.stack + '\nFrom ' + err.stack
+  }
+}
+class TokenizationError extends LiquidError {
+  constructor (message, token) {
+    super(new Error(message), token)
+    this.name = 'TokenizationError'
+    super.update()
+  }
+}
+class ParseError extends LiquidError {
+  constructor (err, token) {
+    super(err, token)
+    this.name = 'ParseError'
+    this.message = err.message
+    super.update()
+  }
+}
+class RenderError extends LiquidError {
+  constructor (err, tpl) {
+    super(err, tpl.token)
+    this.name = 'RenderError'
+    this.message = err.message
+    super.update()
+  }
+
+  static is (obj) {
+    return obj instanceof RenderError
+  }
+}
+class AssertionError extends Error {
+  constructor (message) {
+    super(message)
+    this.name = 'AssertionError'
+    this.message = message + ''
+  }
+}
+function mkContext (token) {
+  const [line] = token.getPosition()
+  const lines = token.input.split('\n')
+  const begin = Math.max(line - 2, 1)
+  const end = Math.min(line + 3, lines.length)
+  const context = range(begin, end + 1)
+    .map(lineNumber => {
+      const indicator = (lineNumber === line) ? '>> ' : '   '
+      const num = padStart(String(lineNumber), String(end).length)
+      const text = lines[lineNumber - 1]
+      return `${indicator}${num}| ${text}`
+    })
+    .join('\n')
+  return context
+}
+function mkMessage (msg, token) {
+  if (token.file) { msg += `, file:${token.file}` }
+  const [line, col] = token.getPosition()
+  msg += `, line:${line}, col:${col}`
+  return msg
+}
+
+function assert (predicate, message) {
+  if (!predicate) {
+    const msg = message ? message() : `expect ${predicate} to be true`
+    throw new AssertionError(msg)
+  }
+}
+
+class FilterToken extends Token {
+  constructor (name, args, input, begin, end, file) {
+    super(TokenKind.Filter, input, begin, end, file)
+    this.name = name
+    this.args = args
+  }
+}
+
+class HashToken extends Token {
+  constructor (input, begin, end, name, value, file) {
+    super(TokenKind.Hash, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.name = name
+    this.value = value
+    this.file = file
+  }
+}
+
+class QuotedToken extends Token {
+  constructor (input, begin, end, file) {
+    super(TokenKind.Quoted, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.file = file
+  }
+}
+
+class HTMLToken extends Token {
+  constructor (input, begin, end, file) {
+    super(TokenKind.HTML, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.file = file
+    this.trimLeft = 0
+    this.trimRight = 0
+  }
+
+  getContent () {
+    return this.input.slice(this.begin + this.trimLeft, this.end - this.trimRight)
+  }
+}
+
+class DelimitedToken extends Token {
+  constructor (kind, content, input, begin, end, trimLeft, trimRight, file) {
+    super(kind, input, begin, end, file)
+    this.trimLeft = false
+    this.trimRight = false
+    this.content = this.getText()
+    const tl = content[0] === '-'
+    const tr = last(content) === '-'
+    this.content = content
+      .slice(tl ? 1 : 0, tr ? -1 : content.length)
+      .trim()
+    this.trimLeft = tl || trimLeft
+    this.trimRight = tr || trimRight
+  }
+}
+
+class TagToken extends DelimitedToken {
+  constructor (input, begin, end, options, file) {
+    const { trimTagLeft, trimTagRight, tagDelimiterLeft, tagDelimiterRight } = options
+    const value = input.slice(begin + tagDelimiterLeft.length, end - tagDelimiterRight.length)
+    super(TokenKind.Tag, value, input, begin, end, trimTagLeft, trimTagRight, file)
+    let nameEnd = 0
+    while (TYPES[this.content.charCodeAt(nameEnd)] & VARIABLE) { nameEnd++ }
+    this.name = this.content.slice(0, nameEnd)
+    if (!this.name) { throw new TokenizationError('illegal tag syntax', this) }
+    let argsBegin = nameEnd
+    while (TYPES[this.content.charCodeAt(argsBegin)] & BLANK) { argsBegin++ }
+    this.args = this.content.slice(argsBegin)
+  }
+}
+
+class RangeToken extends Token {
+  constructor (input, begin, end, lhs, rhs, file) {
+    super(TokenKind.Range, input, begin, end, file)
+    this.input = input
+    this.begin = begin
+    this.end = end
+    this.lhs = lhs
+    this.rhs = rhs
+    this.file = file
+  }
+}
+
+class OutputToken extends DelimitedToken {
+  constructor (input, begin, end, options, file) {
+    const { trimOutputLeft, trimOutputRight, outputDelimiterLeft, outputDelimiterRight } = options
+    const value = input.slice(begin + outputDelimiterLeft.length, end - outputDelimiterRight.length)
+    super(TokenKind.Output, value, input, begin, end, trimOutputLeft, trimOutputRight, file)
+  }
+}
+
+const trie = {
+  a: { n: { d: { end: true, needBoundary: true } } },
+  o: { r: { end: true, needBoundary: true } },
+  c: { o: { n: { t: { a: { i: { n: { s: { end: true, needBoundary: true } } } } } } } },
+  '=': { '=': { end: true } },
+  '!': { '=': { end: true } },
+  '>': { end: true, '=': { end: true } },
+  '<': { end: true, '=': { end: true } }
+}
+function matchOperator (str, begin, end = str.length) {
+  let node = trie
+  let i = begin
+  let info
+  while (node[str[i]] && i < end) {
+    node = node[str[i++]]
+    if (node.end) { info = node }
+  }
+  if (!info) { return -1 }
+  if (info.needBoundary && str.charCodeAt(i) & VARIABLE) { return -1 }
+  return i
+}
+
+class Tokenizer {
+  constructor (input, file = '') {
+    this.input = input
+    this.file = file
+    this.p = 0
+    this.N = input.length
+  }
+
+  * readExpression () {
+    const operand = this.readValue()
+    if (!operand) { return }
+    yield operand
+    while (this.p < this.N) {
+      const operator = this.readOperator()
+      if (!operator) { return }
+      const operand = this.readValue()
+      if (!operand) { return }
+      yield operator
+      yield operand
+    }
+  }
+
+  readOperator () {
+    this.skipBlank()
+    const end = matchOperator(this.input, this.p, this.p + 8)
+    if (end === -1) { return }
+    return new OperatorToken(this.input, this.p, (this.p = end), this.file)
+  }
+
+  readFilters () {
+    const filters = []
+    while (true) {
+      const filter = this.readFilter()
+      if (!filter) { return filters }
+      filters.push(filter)
+    }
+  }
+
+  readFilter () {
+    this.skipBlank()
+    if (this.end()) { return null }
+    assert(this.peek() === '|', () => `unexpected token at ${this.snapshot()}`)
+    this.p++
+    const begin = this.p
+    const name = this.readWord()
+    if (!name.size()) { return null }
+    const args = []
+    this.skipBlank()
+    if (this.peek() === ':') {
+      do {
+        ++this.p
+        const arg = this.readFilterArg()
+        arg && args.push(arg)
+        while (this.p < this.N && this.peek() !== ',' && this.peek() !== '|') { ++this.p }
+      } while (this.peek() === ',')
+    }
+    return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file)
+  }
+
+  readFilterArg () {
+    const key = this.readValue()
+    if (!key) { return }
+    this.skipBlank()
+    if (this.peek() !== ':') { return key }
+    ++this.p
+    const value = this.readValue()
+    return [key.getText(), value]
+  }
+
+  readTopLevelTokens (options = defaultOptions) {
+    const tokens = []
+    while (this.p < this.N) {
+      const token = this.readTopLevelToken(options)
+      tokens.push(token)
+    }
+    whiteSpaceCtrl(tokens, options)
+    return tokens
+  }
+
+  readTopLevelToken (options) {
+    const { tagDelimiterLeft, outputDelimiterLeft } = options
+    if (this.matchWord(tagDelimiterLeft)) { return this.readTagToken(options) }
+    if (this.matchWord(outputDelimiterLeft)) { return this.readOutputToken(options) }
+    return this.readHTMLToken(options)
+  }
+
+  readHTMLToken (options) {
+    const begin = this.p
+    while (this.p < this.N) {
+      const { tagDelimiterLeft, outputDelimiterLeft } = options
+      if (this.matchWord(tagDelimiterLeft)) { break }
+      if (this.matchWord(outputDelimiterLeft)) { break }
+      ++this.p
+    }
+    return new HTMLToken(this.input, begin, this.p, this.file)
+  }
+
+  readTagToken (options) {
+    const { file, input } = this
+    const { tagDelimiterRight } = options
+    const begin = this.p
+    if (this.readTo(tagDelimiterRight) === -1) {
+      this.mkError(`tag ${this.snapshot(begin)} not closed`, begin)
+    }
+    return new TagToken(input, begin, this.p, options, file)
+  }
+
+  readOutputToken (options) {
+    const { file, input } = this
+    const { outputDelimiterRight } = options
+    const begin = this.p
+    if (this.readTo(outputDelimiterRight) === -1) {
+      this.mkError(`output ${this.snapshot(begin)} not closed`, begin)
+    }
+    return new OutputToken(input, begin, this.p, options, file)
+  }
+
+  mkError (msg, begin) {
+    throw new TokenizationError(msg, new WordToken(this.input, begin, this.N, this.file))
+  }
+
+  snapshot (begin = this.p) {
+    return JSON.stringify(ellipsis(this.input.slice(begin), 16))
+  }
+
+  readWord () {
+    this.skipBlank()
+    const begin = this.p
+    while (this.peekType() & VARIABLE) { ++this.p }
+    return new WordToken(this.input, begin, this.p, this.file)
+  }
+
+  readHashes () {
+    const hashes = []
+    while (true) {
+      const hash = this.readHash()
+      if (!hash) { return hashes }
+      hashes.push(hash)
+    }
+  }
+
+  readHash () {
+    this.skipBlank()
+    if (this.peek() === ',') { ++this.p }
+    const begin = this.p
+    const name = this.readWord()
+    if (!name.size()) { return }
+    let value
+    this.skipBlank()
+    if (this.peek() === ':') {
+      ++this.p
+      value = this.readValue()
+    }
+    return new HashToken(this.input, begin, this.p, name, value, this.file)
+  }
+
+  remaining () {
+    return this.input.slice(this.p)
+  }
+
+  advance (i = 1) {
+    this.p += i
+  }
+
+  end () {
+    return this.p >= this.N
+  }
+
+  readTo (end) {
+    while (this.p < this.N) {
+      ++this.p
+      if (this.reverseMatchWord(end)) { return this.p }
+    }
+    return -1
+  }
+
+  readValue () {
+    const value = this.readQuoted() || this.readRange()
+    if (value) { return value }
+    if (this.peek() === '[') {
+      this.p++
+      const prop = this.readQuoted()
+      if (!prop) { return }
+      if (this.peek() !== ']') { return }
+      this.p++
+      return new PropertyAccessToken(prop, [], this.p)
+    }
+    const variable = this.readWord()
+    if (!variable.size()) { return }
+    let isNumber = variable.isNumber(true)
+    const props = []
+    while (true) {
+      if (this.peek() === '[') {
+        isNumber = false
+        this.p++
+        const prop = this.readValue() || new WordToken(this.input, this.p, this.p, this.file)
+        this.readTo(']')
+        props.push(prop)
+      } else if (this.peek() === '.' && this.peek(1) !== '.') { // skip range syntax
+        this.p++
+        const prop = this.readWord()
+        if (!prop.size()) { break }
+        if (!prop.isNumber()) { isNumber = false }
+        props.push(prop)
+      } else { break }
+    }
+    if (!props.length && literalValues.hasOwnProperty(variable.content)) {
+      return new LiteralToken(this.input, variable.begin, variable.end, this.file)
+    }
+    if (isNumber) { return new NumberToken(variable, props[0]) }
+    return new PropertyAccessToken(variable, props, this.p)
+  }
+
+  readRange () {
+    this.skipBlank()
+    const begin = this.p
+    if (this.peek() !== '(') { return }
+    ++this.p
+    const lhs = this.readValueOrThrow()
+    this.p += 2
+    const rhs = this.readValueOrThrow()
+    ++this.p
+    return new RangeToken(this.input, begin, this.p, lhs, rhs, this.file)
+  }
+
+  readValueOrThrow () {
+    const value = this.readValue()
+    assert(value, () => `unexpected token ${this.snapshot()}, value expected`)
+    return value
+  }
+
+  readQuoted () {
+    this.skipBlank()
+    const begin = this.p
+    if (!(this.peekType() & QUOTE)) { return }
+    ++this.p
+    let escaped = false
+    while (this.p < this.N) {
+      ++this.p
+      if (this.input[this.p - 1] === this.input[begin] && !escaped) { break }
+      if (escaped) { escaped = false } else if (this.input[this.p - 1] === '\\') { escaped = true }
+    }
+    return new QuotedToken(this.input, begin, this.p, this.file)
+  }
+
+  readFileName () {
+    const begin = this.p
+    while (!(this.peekType() & BLANK) && this.peek() !== ',' && this.p < this.N) { this.p++ }
+    return new WordToken(this.input, begin, this.p, this.file)
+  }
+
+  matchWord (word) {
+    for (let i = 0; i < word.length; i++) {
+      if (word[i] !== this.input[this.p + i]) { return false }
+    }
+    return true
+  }
+
+  reverseMatchWord (word) {
+    for (let i = 0; i < word.length; i++) {
+      if (word[word.length - 1 - i] !== this.input[this.p - 1 - i]) { return false }
+    }
+    return true
+  }
+
+  peekType (n = 0) {
+    return TYPES[this.input.charCodeAt(this.p + n)]
+  }
+
+  peek (n = 0) {
+    return this.input[this.p + n]
+  }
+
+  skipBlank () {
+    while (this.peekType() & BLANK) { ++this.p }
+  }
+}
+
+class Emitter {
+  constructor () {
+    this.html = ''
+    this.break = false
+    this.continue = false
+  }
+
+  write (html) {
+    this.html += html
+  }
+}
+
+class Render {
+  * renderTemplates (templates, ctx, emitter = new Emitter()) {
+    for (const tpl of templates) {
+      try {
+        const html = yield tpl.render(ctx, emitter)
+        html && emitter.write(html)
+        if (emitter.break || emitter.continue) { break }
+      } catch (e) {
+        const err = RenderError.is(e) ? e : new RenderError(e, tpl)
+        throw err
+      }
+    }
+    return emitter.html
+  }
+}
+
+class ParseStream {
+  constructor (tokens, parseToken) {
+    this.handlers = {}
+    this.stopRequested = false
+    this.tokens = tokens
+    this.parseToken = parseToken
+  }
+
+  on (name, cb) {
+    this.handlers[name] = cb
+    return this
+  }
+
+  trigger (event, arg) {
+    const h = this.handlers[event]
+    return h ? (h(arg), true) : false
+  }
+
+  start () {
+    this.trigger('start')
+    let token
+    while (!this.stopRequested && (token = this.tokens.shift())) {
+      if (this.trigger('token', token)) { continue }
+      if (isTagToken(token) && this.trigger(`tag:${token.name}`, token)) {
+        continue
+      }
+      const template = this.parseToken(token, this.tokens)
+      this.trigger('template', template)
+    }
+    if (!this.stopRequested) { this.trigger('end') }
+    return this
+  }
+
+  stop () {
+    this.stopRequested = true
+    return this
+  }
+}
+
+class TemplateImpl {
+  constructor (token) {
+    this.token = token
+  }
+}
+
+function isComparable (arg) {
+  return arg && isFunction(arg.equals)
+}
+
+function isTruthy (val, ctx) {
+  return !isFalsy(val, ctx)
+}
+function isFalsy (val, ctx) {
+  if (ctx.opts.jsTruthy) {
+    return !val
+  } else {
+    return val === false || undefined === val || val === null
+  }
+}
+
+const operatorImpls = {
+  '==': (l, r) => {
+    if (isComparable(l)) { return l.equals(r) }
+    if (isComparable(r)) { return r.equals(l) }
+    return l === r
+  },
+  '!=': (l, r) => {
+    if (isComparable(l)) { return !l.equals(r) }
+    if (isComparable(r)) { return !r.equals(l) }
+    return l !== r
+  },
+  '>': (l, r) => {
+    if (isComparable(l)) { return l.gt(r) }
+    if (isComparable(r)) { return r.lt(l) }
+    return l > r
+  },
+  '<': (l, r) => {
+    if (isComparable(l)) { return l.lt(r) }
+    if (isComparable(r)) { return r.gt(l) }
+    return l < r
+  },
+  '>=': (l, r) => {
+    if (isComparable(l)) { return l.geq(r) }
+    if (isComparable(r)) { return r.leq(l) }
+    return l >= r
+  },
+  '<=': (l, r) => {
+    if (isComparable(l)) { return l.leq(r) }
+    if (isComparable(r)) { return r.geq(l) }
+    return l <= r
+  },
+  contains: (l, r) => {
+    return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false
+  },
+  and: (l, r, ctx) => isTruthy(l, ctx) && isTruthy(r, ctx),
+  or: (l, r, ctx) => isTruthy(l, ctx) || isTruthy(r, ctx)
+}
+
+class Expression {
+  constructor (str) {
+    this.operands = []
+    const tokenizer = new Tokenizer(str)
+    this.postfix = toPostfix(tokenizer.readExpression())
+  }
+
+  evaluate (ctx) {
+    for (const token of this.postfix) {
+      if (isOperatorToken(token)) {
+        const r = this.operands.pop()
+        const l = this.operands.pop()
+        const result = evalOperatorToken(token, l, r, ctx)
+        this.operands.push(result)
+      } else {
+        this.operands.push(evalToken(token, ctx))
+      }
+    }
+    return this.operands[0]
+  }
+
+  * value (ctx) {
+    return toValue(this.evaluate(ctx))
+  }
+}
+function evalToken (token, ctx) {
+  assert(ctx, () => 'unable to evaluate: context not defined')
+  if (isPropertyAccessToken(token)) {
+    const variable = token.getVariableAsText()
+    const props = token.props.map(prop => evalToken(prop, ctx))
+    return ctx.get([variable, ...props])
+  }
+  if (isRangeToken(token)) { return evalRangeToken(token, ctx) }
+  if (isLiteralToken(token)) { return evalLiteralToken(token) }
+  if (isNumberToken(token)) { return evalNumberToken(token) }
+  if (isWordToken(token)) { return token.getText() }
+  if (isQuotedToken(token)) { return evalQuotedToken(token) }
+}
+function evalNumberToken (token) {
+  const str = token.whole.content + '.' + (token.decimal ? token.decimal.content : '')
+  return Number(str)
+}
+function evalQuotedToken (token) {
+  return parseStringLiteral(token.getText())
+}
+function evalOperatorToken (token, lhs, rhs, ctx) {
+  const impl = operatorImpls[token.operator]
+  return impl(lhs, rhs, ctx)
+}
+function evalLiteralToken (token) {
+  return literalValues[token.literal]
+}
+function evalRangeToken (token, ctx) {
+  const low = evalToken(token.lhs, ctx)
+  const high = evalToken(token.rhs, ctx)
+  return range(+low, +high + 1)
+}
+function * toPostfix (tokens) {
+  const ops = []
+  for (const token of tokens) {
+    if (isOperatorToken(token)) {
+      while (ops.length && ops[ops.length - 1].getPrecedence() > token.getPrecedence()) {
+        yield ops.pop()
+      }
+      ops.push(token)
+    } else { yield token }
+  }
+  while (ops.length) {
+    yield ops.pop()
+  }
+}
+
+/**
+ * Key-Value Pairs Representing Tag Arguments
+ * Example:
+ *    For the markup `, foo:'bar', coo:2 reversed %}`,
+ *    hash['foo'] === 'bar'
+ *    hash['coo'] === 2
+ *    hash['reversed'] === undefined
+ */
+class Hash {
+  constructor (markup) {
+    this.hash = {}
+    const tokenizer = new Tokenizer(markup)
+    for (const hash of tokenizer.readHashes()) {
+      this.hash[hash.name.content] = hash.value
+    }
+  }
+
+  * render (ctx) {
+    const hash = {}
+    for (const key of Object.keys(this.hash)) {
+      hash[key] = yield evalToken(this.hash[key], ctx)
+    }
+    return hash
+  }
+}
+
+function createResolvedThenable (value) {
+  const ret = {
+    then: (resolve) => resolve(value),
+    catch: () => ret
+  }
+  return ret
+}
+function createRejectedThenable (err) {
+  const ret = {
+    then: (resolve, reject) => {
+      if (reject) { return reject(err) }
+      return ret
+    },
+    catch: (reject) => reject(err)
+  }
+  return ret
+}
+function isThenable (val) {
+  return val && isFunction(val.then)
+}
+function isAsyncIterator (val) {
+  return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
+}
+// convert an async iterator to a thenable (Promise compatible)
+function toThenable (val) {
+  if (isThenable(val)) { return val }
+  if (isAsyncIterator(val)) { return reduce() }
+  return createResolvedThenable(val)
+  function reduce (prev) {
+    let state
+    try {
+      state = val.next(prev)
+    } catch (err) {
+      return createRejectedThenable(err)
+    }
+    if (state.done) { return createResolvedThenable(state.value) }
+    return toThenable(state.value).then(reduce, err => {
+      let state
+      try {
+        state = val.throw(err)
+      } catch (e) {
+        return createRejectedThenable(e)
+      }
+      if (state.done) { return createResolvedThenable(state.value) }
+      return reduce(state.value)
+    })
+  }
+}
+function toPromise (val) {
+  return Promise.resolve(toThenable(val))
+}
+// get the value of async iterator in synchronous manner
+function toValue$1 (val) {
+  let ret
+  toThenable(val)
+    .then((x) => {
+      ret = x
+      return createResolvedThenable(ret)
+    })
+    .catch((err) => {
+      throw err
+    })
+  return ret
+}
+
+class Tag extends TemplateImpl {
+  constructor (token, tokens, liquid) {
+    super(token)
+    this.name = token.name
+    const impl = liquid.tags.get(token.name)
+    this.impl = Object.create(impl)
+    this.impl.liquid = liquid
+    if (this.impl.parse) {
+      this.impl.parse(token, tokens)
+    }
+  }
+
+  * render (ctx, emitter) {
+    const hash = yield new Hash(this.token.args).render(ctx)
+    const impl = this.impl
+    if (isFunction(impl.render)) { return yield impl.render(ctx, emitter, hash) }
+  }
+}
+Tag.impls = {}
+
+function isKeyValuePair (arr) {
+  return isArray(arr)
+}
+
+class Filter {
+  constructor (name, impl, args) {
+    this.name = name
+    this.impl = impl || identify
+    this.args = args
+  }
+
+  * render (value, context) {
+    const argv = []
+    for (const arg of this.args) {
+      if (isKeyValuePair(arg)) { argv.push([arg[0], yield evalToken(arg[1], context)]) } else { argv.push(yield evalToken(arg, context)) }
+    }
+    return yield this.impl.apply({ context }, [value, ...argv])
+  }
+}
+
+class Value {
+  /**
+     * @param str the value to be valuated, eg.: "foobar" | truncate: 3
+     */
+  constructor (str, filterMap) {
+    this.filterMap = filterMap
+    this.filters = []
+    const tokenizer = new Tokenizer(str)
+    this.initial = tokenizer.readValue()
+    this.filters = tokenizer.readFilters().map(({ name, args }) => new Filter(name, this.filterMap.get(name), args))
+  }
+
+  * value (ctx) {
+    let val = yield evalToken(this.initial, ctx)
+    for (const filter of this.filters) {
+      val = yield filter.render(val, ctx)
+    }
+    return val
+  }
+}
+
+class Output extends TemplateImpl {
+  constructor (token, filters) {
+    super(token)
+    this.value = new Value(token.content, filters)
+  }
+
+  * render (ctx, emitter) {
+    const val = yield this.value.value(ctx)
+    emitter.write(stringify(toValue(val)))
+  }
+}
+
+class HTML extends TemplateImpl {
+  constructor (token) {
+    super(token)
+    this.str = token.getContent()
+  }
+
+  * render (ctx, emitter) {
+    emitter.write(this.str)
+  }
+}
+
+class Parser {
+  constructor (liquid) {
+    this.liquid = liquid
+  }
+
+  parse (tokens) {
+    let token
+    const templates = []
+    while ((token = tokens.shift())) {
+      templates.push(this.parseToken(token, tokens))
+    }
+    return templates
+  }
+
+  parseToken (token, remainTokens) {
+    try {
+      if (isTagToken(token)) {
+        return new Tag(token, remainTokens, this.liquid)
+      }
+      if (isOutputToken(token)) {
+        return new Output(token, this.liquid.filters)
+      }
+      return new HTML(token)
+    } catch (e) {
+      throw new ParseError(e, token)
+    }
+  }
+
+  parseStream (tokens) {
+    return new ParseStream(tokens, (token, tokens) => this.parseToken(token, tokens))
+  }
+}
+
+var assign = {
+  parse: function (token) {
+    const tokenizer = new Tokenizer(token.args)
+    this.key = tokenizer.readWord().content
+    tokenizer.skipBlank()
+    assert(tokenizer.peek() === '=', () => `illegal token ${token.getText()}`)
+    tokenizer.advance()
+    this.value = tokenizer.remaining()
+  },
+  render: function * (ctx) {
+    ctx.bottom()[this.key] = yield this.liquid._evalValue(this.value, ctx)
+  }
+}
+
+function toEnumerable (val) {
+  if (isArray(val)) { return val }
+  if (isString(val) && val.length > 0) { return [val] }
+  if (isObject(val)) { return Object.keys(val).map((key) => [key, val[key]]) }
+  return []
+}
+function toArray (val) {
+  if (isArray(val)) { return val }
+  return [val]
+}
+
+class ForloopDrop extends Drop {
+  constructor (length) {
+    super()
+    this.i = 0
+    this.length = length
+  }
+
+  next () {
+    this.i++
+  }
+
+  index0 () {
+    return this.i
+  }
+
+  index () {
+    return this.i + 1
+  }
+
+  first () {
+    return this.i === 0
+  }
+
+  last () {
+    return this.i === this.length - 1
+  }
+
+  rindex () {
+    return this.length - this.i
+  }
+
+  rindex0 () {
+    return this.length - this.i - 1
+  }
+
+  valueOf () {
+    return JSON.stringify(this)
+  }
+}
+
+var For = {
+  type: 'block',
+  parse: function (token, remainTokens) {
+    const toknenizer = new Tokenizer(token.args)
+    const variable = toknenizer.readWord()
+    const inStr = toknenizer.readWord()
+    const collection = toknenizer.readValue()
+    assert(variable.size() && inStr.content === 'in' && collection, () => `illegal tag: ${token.getText()}`)
+    this.variable = variable.content
+    this.collection = collection
+    this.hash = new Hash(toknenizer.remaining())
+    this.templates = []
+    this.elseTemplates = []
+    let p
+    const stream = this.liquid.parser.parseStream(remainTokens)
+      .on('start', () => (p = this.templates))
+      .on('tag:else', () => (p = this.elseTemplates))
+      .on('tag:endfor', () => stream.stop())
+      .on('template', (tpl) => p.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${token.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx, emitter) {
+    const r = this.liquid.renderer
+    let collection = toEnumerable(yield evalToken(this.collection, ctx))
+    if (!collection.length) {
+      yield r.renderTemplates(this.elseTemplates, ctx, emitter)
+      return
+    }
+    const hash = yield this.hash.render(ctx)
+    const offset = hash.offset || 0
+    const limit = (hash.limit === undefined) ? collection.length : hash.limit
+    collection = collection.slice(offset, offset + limit)
+    if ('reversed' in hash) { collection.reverse() }
+    const scope = { forloop: new ForloopDrop(collection.length) }
+    ctx.push(scope)
+    for (const item of collection) {
+      scope[this.variable] = item
+      yield r.renderTemplates(this.templates, ctx, emitter)
+      if (emitter.break) {
+        emitter.break = false
+        break
+      }
+      emitter.continue = false
+      scope.forloop.next()
+    }
+    ctx.pop()
+  }
+}
+
+var capture = {
+  parse: function (tagToken, remainTokens) {
+    const tokenizer = new Tokenizer(tagToken.args)
+    this.variable = readVariableName(tokenizer)
+    assert(this.variable, () => `${tagToken.args} not valid identifier`)
+    this.templates = []
+    const stream = this.liquid.parser.parseStream(remainTokens)
+    stream.on('tag:endcapture', () => stream.stop())
+      .on('template', (tpl) => this.templates.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx) {
+    const r = this.liquid.renderer
+    const html = yield r.renderTemplates(this.templates, ctx)
+    ctx.bottom()[this.variable] = html
+  }
+}
+function readVariableName (tokenizer) {
+  const word = tokenizer.readWord().content
+  if (word) { return word }
+  const quoted = tokenizer.readQuoted()
+  if (quoted) { return evalQuotedToken(quoted) }
+}
+
+var Case = {
+  parse: function (tagToken, remainTokens) {
+    this.cond = tagToken.args
+    this.cases = []
+    this.elseTemplates = []
+    let p = []
+    const stream = this.liquid.parser.parseStream(remainTokens)
+      .on('tag:when', (token) => {
+        this.cases.push({
+          val: token.args,
+          templates: p = []
+        })
+      })
+      .on('tag:else', () => (p = this.elseTemplates))
+      .on('tag:endcase', () => stream.stop())
+      .on('template', (tpl) => p.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx, emitter) {
+    const r = this.liquid.renderer
+    const cond = yield new Expression(this.cond).value(ctx)
+    for (let i = 0; i < this.cases.length; i++) {
+      const branch = this.cases[i]
+      const val = yield new Expression(branch.val).value(ctx)
+      if (val === cond) {
+        yield r.renderTemplates(branch.templates, ctx, emitter)
+        return
+      }
+    }
+    yield r.renderTemplates(this.elseTemplates, ctx, emitter)
+  }
+}
+
+var comment = {
+  parse: function (tagToken, remainTokens) {
+    const stream = this.liquid.parser.parseStream(remainTokens)
+    stream
+      .on('token', (token) => {
+        if (token.name === 'endcomment') { stream.stop() }
+      })
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  }
+}
+
+var BlockMode;
+(function (BlockMode) {
+  /* store rendered html into blocks */
+  BlockMode[BlockMode.OUTPUT = 0] = 'OUTPUT'
+  /* output rendered html directly */
+  BlockMode[BlockMode.STORE = 1] = 'STORE'
+})(BlockMode || (BlockMode = {}))
+var BlockMode$1 = BlockMode
+
+var include = {
+  parse: function (token) {
+    const args = token.args
+    const tokenizer = new Tokenizer(args)
+    this.file = this.liquid.options.dynamicPartials
+      ? tokenizer.readValue()
+      : tokenizer.readFileName()
+    assert(this.file, () => `illegal argument "${token.args}"`)
+    const begin = tokenizer.p
+    const withStr = tokenizer.readWord()
+    if (withStr.content === 'with') {
+      tokenizer.skipBlank()
+      if (tokenizer.peek() !== ':') {
+        this.withVar = tokenizer.readValue()
+      } else { tokenizer.p = begin }
+    } else { tokenizer.p = begin }
+    this.hash = new Hash(tokenizer.remaining())
+  },
+  render: function * (ctx, emitter) {
+    const { liquid, hash, withVar, file } = this
+    const { renderer } = liquid
+    const filepath = ctx.opts.dynamicPartials
+      ? (isQuotedToken(file)
+          ? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx)
+          : yield evalToken(file, ctx))
+      : file.getText()
+    assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
+    const saved = ctx.saveRegister('blocks', 'blockMode')
+    ctx.setRegister('blocks', {})
+    ctx.setRegister('blockMode', BlockMode$1.OUTPUT)
+    const scope = yield hash.render(ctx)
+    if (withVar) { scope[filepath] = evalToken(withVar, ctx) }
+    const templates = yield liquid._parseFile(filepath, ctx.opts, ctx.sync)
+    ctx.push(scope)
+    yield renderer.renderTemplates(templates, ctx, emitter)
+    ctx.pop()
+    ctx.restoreRegister(saved)
+  }
+}
+
+var render = {
+  parse: function (token) {
+    const args = token.args
+    const tokenizer = new Tokenizer(args)
+    this.file = this.liquid.options.dynamicPartials
+      ? tokenizer.readValue()
+      : tokenizer.readFileName()
+    assert(this.file, () => `illegal argument "${token.args}"`)
+    while (!tokenizer.end()) {
+      tokenizer.skipBlank()
+      const begin = tokenizer.p
+      const keyword = tokenizer.readWord()
+      if (keyword.content === 'with' || keyword.content === 'for') {
+        tokenizer.skipBlank()
+        if (tokenizer.peek() !== ':') {
+          const value = tokenizer.readValue()
+          if (value) {
+            const beforeAs = tokenizer.p
+            const asStr = tokenizer.readWord()
+            let alias
+            if (asStr.content === 'as') { alias = tokenizer.readWord() } else { tokenizer.p = beforeAs }
+            this[keyword.content] = { value, alias: alias && alias.content }
+            tokenizer.skipBlank()
+            if (tokenizer.peek() === ',') { tokenizer.advance() }
+            continue
+          }
+        }
+      }
+      tokenizer.p = begin
+      break
+    }
+    this.hash = new Hash(tokenizer.remaining())
+  },
+  render: function * (ctx, emitter) {
+    const { liquid, file, hash } = this
+    const { renderer } = liquid
+    const filepath = ctx.opts.dynamicPartials
+      ? (isQuotedToken(file)
+          ? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx)
+          : evalToken(file, ctx))
+      : file.getText()
+    assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
+    const childCtx = new Context({}, ctx.opts, ctx.sync)
+    const scope = yield hash.render(ctx)
+    if (this.with) {
+      const { value, alias } = this.with
+      scope[alias || filepath] = evalToken(value, ctx)
+    }
+    childCtx.push(scope)
+    if (this.for) {
+      const { value, alias } = this.for
+      let collection = evalToken(value, ctx)
+      collection = toEnumerable(collection)
+      scope.forloop = new ForloopDrop(collection.length)
+      for (const item of collection) {
+        scope[alias] = item
+        const templates = yield liquid._parseFile(filepath, childCtx.opts, childCtx.sync)
+        yield renderer.renderTemplates(templates, childCtx, emitter)
+        scope.forloop.next()
+      }
+    } else {
+      const templates = yield liquid._parseFile(filepath, childCtx.opts, childCtx.sync)
+      yield renderer.renderTemplates(templates, childCtx, emitter)
+    }
+  }
+}
+
+var decrement = {
+  parse: function (token) {
+    const tokenizer = new Tokenizer(token.args)
+    this.variable = tokenizer.readWord().content
+  },
+  render: function (context, emitter) {
+    const scope = context.environments
+    if (!isNumber(scope[this.variable])) {
+      scope[this.variable] = 0
+    }
+    emitter.write(stringify(--scope[this.variable]))
+  }
+}
+
+var cycle = {
+  parse: function (tagToken) {
+    const tokenizer = new Tokenizer(tagToken.args)
+    const group = tokenizer.readValue()
+    tokenizer.skipBlank()
+    this.candidates = []
+    if (group) {
+      if (tokenizer.peek() === ':') {
+        this.group = group
+        tokenizer.advance()
+      } else { this.candidates.push(group) }
+    }
+    while (!tokenizer.end()) {
+      const value = tokenizer.readValue()
+      if (value) { this.candidates.push(value) }
+      tokenizer.readTo(',')
+    }
+    assert(this.candidates.length, () => `empty candidates: ${tagToken.getText()}`)
+  },
+  render: function (ctx, emitter) {
+    const group = evalToken(this.group, ctx)
+    const fingerprint = `cycle:${group}:` + this.candidates.join(',')
+    const groups = ctx.getRegister('cycle')
+    let idx = groups[fingerprint]
+    if (idx === undefined) {
+      idx = groups[fingerprint] = 0
+    }
+    const candidate = this.candidates[idx]
+    idx = (idx + 1) % this.candidates.length
+    groups[fingerprint] = idx
+    const html = evalToken(candidate, ctx)
+    emitter.write(html)
+  }
+}
+
+var If = {
+  parse: function (tagToken, remainTokens) {
+    this.branches = []
+    this.elseTemplates = []
+    let p
+    const stream = this.liquid.parser.parseStream(remainTokens)
+      .on('start', () => this.branches.push({
+        cond: tagToken.args,
+        templates: (p = [])
+      }))
+      .on('tag:elsif', (token) => {
+        this.branches.push({
+          cond: token.args,
+          templates: p = []
+        })
+      })
+      .on('tag:else', () => (p = this.elseTemplates))
+      .on('tag:endif', () => stream.stop())
+      .on('template', (tpl) => p.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx, emitter) {
+    const r = this.liquid.renderer
+    for (const branch of this.branches) {
+      const cond = yield new Expression(branch.cond).value(ctx)
+      if (isTruthy(cond, ctx)) {
+        yield r.renderTemplates(branch.templates, ctx, emitter)
+        return
+      }
+    }
+    yield r.renderTemplates(this.elseTemplates, ctx, emitter)
+  }
+}
+
+var increment = {
+  parse: function (token) {
+    const tokenizer = new Tokenizer(token.args)
+    this.variable = tokenizer.readWord().content
+  },
+  render: function (context, emitter) {
+    const scope = context.environments
+    if (!isNumber(scope[this.variable])) {
+      scope[this.variable] = 0
+    }
+    const val = scope[this.variable]
+    scope[this.variable]++
+    emitter.write(stringify(val))
+  }
+}
+
+var layout = {
+  parse: function (token, remainTokens) {
+    const tokenizer = new Tokenizer(token.args)
+    const file = this.liquid.options.dynamicPartials ? tokenizer.readValue() : tokenizer.readFileName()
+    assert(file, () => `illegal argument "${token.args}"`)
+    this.file = file
+    this.hash = new Hash(tokenizer.remaining())
+    this.tpls = this.liquid.parser.parse(remainTokens)
+  },
+  render: function * (ctx, emitter) {
+    const { liquid, hash, file } = this
+    const { renderer } = liquid
+    const filepath = ctx.opts.dynamicPartials
+      ? (isQuotedToken(file)
+          ? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx)
+          : evalToken(this.file, ctx))
+      : file.getText()
+    assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
+    // render the remaining tokens immediately
+    ctx.setRegister('blockMode', BlockMode$1.STORE)
+    const blocks = ctx.getRegister('blocks')
+    const html = yield renderer.renderTemplates(this.tpls, ctx)
+    if (blocks[''] === undefined) { blocks[''] = html }
+    const templates = yield liquid._parseFile(filepath, ctx.opts, ctx.sync)
+    ctx.push(yield hash.render(ctx))
+    ctx.setRegister('blockMode', BlockMode$1.OUTPUT)
+    const partial = yield renderer.renderTemplates(templates, ctx)
+    ctx.pop()
+    emitter.write(partial)
+  }
+}
+
+var block = {
+  parse: function (token, remainTokens) {
+    const match = /\w+/.exec(token.args)
+    this.block = match ? match[0] : ''
+    this.tpls = []
+    const stream = this.liquid.parser.parseStream(remainTokens)
+      .on('tag:endblock', () => stream.stop())
+      .on('template', (tpl) => this.tpls.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${token.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx, emitter) {
+    const blocks = ctx.getRegister('blocks')
+    const childDefined = blocks[this.block]
+    const r = this.liquid.renderer
+    const html = childDefined !== undefined
+      ? childDefined
+      : yield r.renderTemplates(this.tpls, ctx)
+    if (ctx.getRegister('blockMode', BlockMode$1.OUTPUT) === BlockMode$1.STORE) {
+      blocks[this.block] = html
+      return
+    }
+    emitter.write(html)
+  }
+}
+
+var raw = {
+  parse: function (tagToken, remainTokens) {
+    this.tokens = []
+    const stream = this.liquid.parser.parseStream(remainTokens)
+    stream
+      .on('token', (token) => {
+        if (token.name === 'endraw') { stream.stop() } else { this.tokens.push(token) }
+      })
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function () {
+    return this.tokens.map((token) => token.getText()).join('')
+  }
+}
+
+class TablerowloopDrop extends ForloopDrop {
+  constructor (length, cols) {
+    super(length)
+    this.length = length
+    this.cols = cols
+  }
+
+  row () {
+    return Math.floor(this.i / this.cols) + 1
+  }
+
+  col0 () {
+    return (this.i % this.cols)
+  }
+
+  col () {
+    return this.col0() + 1
+  }
+
+  col_first () {
+    return this.col0() === 0
+  }
+
+  col_last () {
+    return this.col() === this.cols
+  }
+}
+
+var tablerow = {
+  parse: function (tagToken, remainTokens) {
+    const tokenizer = new Tokenizer(tagToken.args)
+    this.variable = tokenizer.readWord()
+    tokenizer.skipBlank()
+    const tmp = tokenizer.readWord()
+    assert(tmp && tmp.content === 'in', () => `illegal tag: ${tagToken.getText()}`)
+    this.collection = tokenizer.readValue()
+    this.hash = new Hash(tokenizer.remaining())
+    this.templates = []
+    let p
+    const stream = this.liquid.parser.parseStream(remainTokens)
+      .on('start', () => (p = this.templates))
+      .on('tag:endtablerow', () => stream.stop())
+      .on('template', (tpl) => p.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx, emitter) {
+    let collection = toEnumerable(yield evalToken(this.collection, ctx))
+    const hash = yield this.hash.render(ctx)
+    const offset = hash.offset || 0
+    const limit = (hash.limit === undefined) ? collection.length : hash.limit
+    collection = collection.slice(offset, offset + limit)
+    const cols = hash.cols || collection.length
+    const r = this.liquid.renderer
+    const tablerowloop = new TablerowloopDrop(collection.length, cols)
+    const scope = { tablerowloop }
+    ctx.push(scope)
+    for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
+      scope[this.variable.content] = collection[idx]
+      if (tablerowloop.col0() === 0) {
+        if (tablerowloop.row() !== 1) { emitter.write('</tr>') }
+        emitter.write(`<tr class="row${tablerowloop.row()}">`)
+      }
+      emitter.write(`<td class="col${tablerowloop.col()}">`)
+      yield r.renderTemplates(this.templates, ctx, emitter)
+      emitter.write('</td>')
+    }
+    if (collection.length) { emitter.write('</tr>') }
+    ctx.pop()
+  }
+}
+
+var unless = {
+  parse: function (tagToken, remainTokens) {
+    this.templates = []
+    this.elseTemplates = []
+    let p
+    const stream = this.liquid.parser.parseStream(remainTokens)
+      .on('start', () => {
+        p = this.templates
+        this.cond = tagToken.args
+      })
+      .on('tag:else', () => (p = this.elseTemplates))
+      .on('tag:endunless', () => stream.stop())
+      .on('template', (tpl) => p.push(tpl))
+      .on('end', () => {
+        throw new Error(`tag ${tagToken.getText()} not closed`)
+      })
+    stream.start()
+  },
+  render: function * (ctx, emitter) {
+    const r = this.liquid.renderer
+    const cond = yield new Expression(this.cond).value(ctx)
+    yield (isFalsy(cond, ctx)
+      ? r.renderTemplates(this.templates, ctx, emitter)
+      : r.renderTemplates(this.elseTemplates, ctx, emitter))
+  }
+}
+
+var Break = {
+  render: function (ctx, emitter) {
+    emitter.break = true
+  }
+}
+
+var Continue = {
+  render: function (ctx, emitter) {
+    emitter.continue = true
+  }
+}
+
+const tags = {
+  assign, for: For, capture, case: Case, comment, include, render, decrement, increment, cycle, if: If, layout, block, raw, tablerow, unless, break: Break, continue: Continue
+}
+
+const escapeMap = {
+  '&': '&amp;',
+  '<': '&lt;',
+  '>': '&gt;',
+  '"': '&#34;',
+  "'": '&#39;'
+}
+const unescapeMap = {
+  '&amp;': '&',
+  '&lt;': '<',
+  '&gt;': '>',
+  '&#34;': '"',
+  '&#39;': "'"
+}
+function escape (str) {
+  return stringify(str).replace(/&|<|>|"|'/g, m => escapeMap[m])
+}
+function unescape (str) {
+  return String(str).replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m])
+}
+function escapeOnce (str) {
+  return escape(unescape(str))
+}
+function newlineToBr (v) {
+  return v.replace(/\n/g, '<br/>')
+}
+function stripHtml (v) {
+  return v.replace(/<script.*?<\/script>|<!--.*?-->|<style.*?<\/style>|<.*?>/g, '')
+}
+
+const abs = Math.abs
+const atLeast = Math.max
+const atMost = Math.min
+const ceil = Math.ceil
+const dividedBy = (v, arg) => v / arg
+const floor = Math.floor
+const minus = (v, arg) => v - arg
+const modulo = (v, arg) => v % arg
+const times = (v, arg) => v * arg
+function round (v, arg = 0) {
+  const amp = Math.pow(10, arg)
+  return Math.round(v * amp) / amp
+}
+function plus (v, arg) {
+  return Number(v) + Number(arg)
+}
+function sortNatural (input, property) {
+  if (!input || !input.sort) { return [] }
+  if (property !== undefined) {
+    return [...input].sort((lhs, rhs) => caseInsensitiveCompare(lhs[property], rhs[property]))
+  }
+  return [...input].sort(caseInsensitiveCompare)
+}
+
+const urlDecode = (x) => x.split('+').map(decodeURIComponent).join(' ')
+const urlEncode = (x) => x.split(' ').map(encodeURIComponent).join('+')
+
+const join = (v, arg) => v.join(arg === undefined ? ' ' : arg)
+const last$1 = (v) => isArray(v) ? last(v) : ''
+const first = (v) => isArray(v) ? v[0] : ''
+const reverse = (v) => [...v].reverse()
+function sort (arr, property) {
+  const getValue = (obj) => property ? this.context.getFromScope(obj, property.split('.')) : obj
+  return toArray(arr).sort((lhs, rhs) => {
+    lhs = getValue(lhs)
+    rhs = getValue(rhs)
+    return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)
+  })
+}
+const size = (v) => (v && v.length) || 0
+function map (arr, property) {
+  return toArray(arr).map(obj => this.context.getFromScope(obj, property.split('.')))
+}
+function concat (v, arg) {
+  return toArray(v).concat(arg)
+}
+function slice (v, begin, length = 1) {
+  begin = begin < 0 ? v.length + begin : begin
+  return v.slice(begin, begin + length)
+}
+function where (arr, property, expected) {
+  return toArray(arr).filter(obj => {
+    const value = this.context.getFromScope(obj, String(property).split('.'))
+    return expected === undefined ? isTruthy(value, this.context) : value === expected
+  })
+}
+function uniq (arr) {
+  const u = {}
+  return (arr || []).filter(val => {
+    if (u.hasOwnProperty(String(val))) { return false }
+    u[String(val)] = true
+    return true
+  })
+}
+
+const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/
+const monthNames = [
+  'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
+  'September', 'October', 'November', 'December'
+]
+const dayNames = [
+  'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
+]
+const monthNamesShort = monthNames.map(abbr)
+const dayNamesShort = dayNames.map(abbr)
+const suffixes = {
+  1: 'st',
+  2: 'nd',
+  3: 'rd',
+  default: 'th'
+}
+function abbr (str) {
+  return str.slice(0, 3)
+}
+// prototype extensions
+function daysInMonth (d) {
+  const feb = isLeapYear(d) ? 29 : 28
+  return [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+}
+function getDayOfYear (d) {
+  let num = 0
+  for (let i = 0; i < d.getMonth(); ++i) {
+    num += daysInMonth(d)[i]
+  }
+  return num + d.getDate()
+}
+function getWeekOfYear (d, startDay) {
+  // Skip to startDay of this week
+  const now = getDayOfYear(d) + (startDay - d.getDay())
+  // Find the first startDay of the year
+  const jan1 = new Date(d.getFullYear(), 0, 1)
+  const then = (7 - jan1.getDay() + startDay)
+  return String(Math.floor((now - then) / 7) + 1)
+}
+function isLeapYear (d) {
+  const year = d.getFullYear()
+  return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)))
+}
+function getSuffix (d) {
+  const str = d.getDate().toString()
+  const index = parseInt(str.slice(-1))
+  return suffixes[index] || suffixes.default
+}
+function century (d) {
+  return parseInt(d.getFullYear().toString().substring(0, 2), 10)
+}
+// default to 0
+const padWidths = {
+  d: 2,
+  e: 2,
+  H: 2,
+  I: 2,
+  j: 3,
+  k: 2,
+  l: 2,
+  L: 3,
+  m: 2,
+  M: 2,
+  S: 2,
+  U: 2,
+  W: 2
+}
+// default to '0'
+const padChars = {
+  a: ' ',
+  A: ' ',
+  b: ' ',
+  B: ' ',
+  c: ' ',
+  e: ' ',
+  k: ' ',
+  l: ' ',
+  p: ' ',
+  P: ' '
+}
+const formatCodes = {
+  a: (d) => dayNamesShort[d.getDay()],
+  A: (d) => dayNames[d.getDay()],
+  b: (d) => monthNamesShort[d.getMonth()],
+  B: (d) => monthNames[d.getMonth()],
+  c: (d) => d.toLocaleString(),
+  C: (d) => century(d),
+  d: (d) => d.getDate(),
+  e: (d) => d.getDate(),
+  H: (d) => d.getHours(),
+  I: (d) => String(d.getHours() % 12 || 12),
+  j: (d) => getDayOfYear(d),
+  k: (d) => d.getHours(),
+  l: (d) => String(d.getHours() % 12 || 12),
+  L: (d) => d.getMilliseconds(),
+  m: (d) => d.getMonth() + 1,
+  M: (d) => d.getMinutes(),
+  N: (d, opts) => {
+    const width = Number(opts.width) || 9
+    const str = String(d.getMilliseconds()).substr(0, width)
+    return padEnd(str, width, '0')
+  },
+  p: (d) => (d.getHours() < 12 ? 'AM' : 'PM'),
+  P: (d) => (d.getHours() < 12 ? 'am' : 'pm'),
+  q: (d) => getSuffix(d),
+  s: (d) => Math.round(d.valueOf() / 1000),
+  S: (d) => d.getSeconds(),
+  u: (d) => d.getDay() || 7,
+  U: (d) => getWeekOfYear(d, 0),
+  w: (d) => d.getDay(),
+  W: (d) => getWeekOfYear(d, 1),
+  x: (d) => d.toLocaleDateString(),
+  X: (d) => d.toLocaleTimeString(),
+  y: (d) => d.getFullYear().toString().substring(2, 4),
+  Y: (d) => d.getFullYear(),
+  z: (d, opts) => {
+    const offset = d.getTimezoneOffset()
+    const nOffset = Math.abs(offset)
+    const h = Math.floor(nOffset / 60)
+    const m = nOffset % 60
+    return (offset > 0 ? '-' : '+') +
+            padStart(h, 2, '0') +
+            (opts.flags[':'] ? ':' : '') +
+            padStart(m, 2, '0')
+  },
+  t: () => '\t',
+  n: () => '\n',
+  '%': () => '%'
+}
+formatCodes.h = formatCodes.b
+function strftime (d, formatStr) {
+  let output = ''
+  let remaining = formatStr
+  let match
+  while ((match = rFormat.exec(remaining))) {
+    output += remaining.slice(0, match.index)
+    remaining = remaining.slice(match.index + match[0].length)
+    output += format(d, match)
+  }
+  return output + remaining
+}
+function format (d, match) {
+  const [input, flagStr = '', width, modifier, conversion] = match
+  const convert = formatCodes[conversion]
+  if (!convert) { return input }
+  const flags = {}
+  for (const flag of flagStr) { flags[flag] = true }
+  let ret = String(convert(d, { flags, width, modifier }))
+  let padChar = padChars[conversion] || '0'
+  let padWidth = width || padWidths[conversion] || 0
+  if (flags['^']) { ret = ret.toUpperCase() } else if (flags['#']) { ret = changeCase(ret) }
+  if (flags._) { padChar = ' ' } else if (flags['0']) { padChar = '0' }
+  if (flags['-']) { padWidth = 0 }
+  return padStart(ret, padWidth, padChar)
+}
+
+function date (v, arg) {
+  let date = v
+  if (v === 'now' || v === 'today') {
+    date = new Date()
+  } else if (isNumber(v)) {
+    date = new Date(v * 1000)
+  } else if (isString(v)) {
+    date = /^\d+$/.test(v) ? new Date(+v * 1000) : new Date(v)
+  }
+  return isValidDate(date) ? strftime(date, arg) : v
+}
+function isValidDate (date) {
+  return date instanceof Date && !isNaN(date.getTime())
+}
+
+function Default (v, arg) {
+  if (isArray(v) || isString(v)) { return v.length ? v : arg }
+  return isFalsy(toValue(v), this.context) ? arg : v
+}
+function json (v) {
+  return JSON.stringify(v)
+}
+
+/**
+ * String related filters
+ *
+ * * prefer stringify() to String() since `undefined`, `null` should eval ''
+ */
+function append (v, arg) {
+  assert(arg !== undefined, () => 'append expect 2 arguments')
+  return stringify(v) + stringify(arg)
+}
+function prepend (v, arg) {
+  assert(arg !== undefined, () => 'prepend expect 2 arguments')
+  return stringify(arg) + stringify(v)
+}
+function lstrip (v) {
+  return stringify(v).replace(/^\s+/, '')
+}
+function downcase (v) {
+  return stringify(v).toLowerCase()
+}
+function upcase (str) {
+  return stringify(str).toUpperCase()
+}
+function remove (v, arg) {
+  return stringify(v).split(arg).join('')
+}
+function removeFirst (v, l) {
+  return stringify(v).replace(l, '')
+}
+function rstrip (str) {
+  return stringify(str).replace(/\s+$/, '')
+}
+function split (v, arg) {
+  return stringify(v).split(arg)
+}
+function strip (v) {
+  return stringify(v).trim()
+}
+function stripNewlines (v) {
+  return stringify(v).replace(/\n/g, '')
+}
+function capitalize (str) {
+  str = stringify(str)
+  return str.charAt(0).toUpperCase() + str.slice(1)
+}
+function replace (v, pattern, replacement) {
+  return stringify(v).split(pattern).join(replacement)
+}
+function replaceFirst (v, arg1, arg2) {
+  return stringify(v).replace(arg1, arg2)
+}
+function truncate (v, l = 50, o = '...') {
+  v = stringify(v)
+  if (v.length <= l) { return v }
+  return v.substr(0, l - o.length) + o
+}
+function truncatewords (v, l = 15, o = '...') {
+  const arr = v.split(/\s+/)
+  let ret = arr.slice(0, l).join(' ')
+  if (arr.length >= l) { ret += o }
+  return ret
+}
+
+var builtinFilters = /* #__PURE__ */Object.freeze({
+  escape: escape,
+  escapeOnce: escapeOnce,
+  newlineToBr: newlineToBr,
+  stripHtml: stripHtml,
+  abs: abs,
+  atLeast: atLeast,
+  atMost: atMost,
+  ceil: ceil,
+  dividedBy: dividedBy,
+  floor: floor,
+  minus: minus,
+  modulo: modulo,
+  times: times,
+  round: round,
+  plus: plus,
+  sortNatural: sortNatural,
+  urlDecode: urlDecode,
+  urlEncode: urlEncode,
+  join: join,
+  last: last$1,
+  first: first,
+  reverse: reverse,
+  sort: sort,
+  size: size,
+  map: map,
+  concat: concat,
+  slice: slice,
+  where: where,
+  uniq: uniq,
+  date: date,
+  Default: Default,
+  json: json,
+  append: append,
+  prepend: prepend,
+  lstrip: lstrip,
+  downcase: downcase,
+  upcase: upcase,
+  remove: remove,
+  removeFirst: removeFirst,
+  rstrip: rstrip,
+  split: split,
+  strip: strip,
+  stripNewlines: stripNewlines,
+  capitalize: capitalize,
+  replace: replace,
+  replaceFirst: replaceFirst,
+  truncate: truncate,
+  truncatewords: truncatewords
+})
+
+class TagMap {
+  constructor () {
+    this.impls = {}
+  }
+
+  get (name) {
+    const impl = this.impls[name]
+    assert(impl, () => `tag "${name}" not found`)
+    return impl
+  }
+
+  set (name, impl) {
+    this.impls[name] = impl
+  }
+}
+
+class FilterMap {
+  constructor (strictFilters) {
+    this.strictFilters = strictFilters
+    this.impls = {}
+  }
+
+  get (name) {
+    const impl = this.impls[name]
+    assert(impl || !this.strictFilters, () => `undefined filter: ${name}`)
+    return impl
+  }
+
+  set (name, impl) {
+    this.impls[name] = impl
+  }
+
+  create (name, args) {
+    return new Filter(name, this.get(name), args)
+  }
+}
+
+class Liquid {
+  constructor (opts = {}) {
+    this.options = applyDefault(normalize(opts))
+    this.parser = new Parser(this)
+    this.renderer = new Render()
+    this.fs = opts.fs || fs
+    this.filters = new FilterMap(this.options.strictFilters)
+    this.tags = new TagMap()
+    forOwn(tags, (conf, name) => this.registerTag(snakeCase(name), conf))
+    forOwn(builtinFilters, (handler, name) => this.registerFilter(snakeCase(name), handler))
+  }
+
+  parse (html, filepath) {
+    const tokenizer = new Tokenizer(html, filepath)
+    const tokens = tokenizer.readTopLevelTokens(this.options)
+    return this.parser.parse(tokens)
+  }
+
+  _render (tpl, scope, opts, sync) {
+    const options = Object.assign({}, this.options, normalize(opts))
+    const ctx = new Context(scope, options, sync)
+    return this.renderer.renderTemplates(tpl, ctx)
+  }
+
+  async render (tpl, scope, opts) {
+    return toPromise(this._render(tpl, scope, opts, false))
+  }
+
+  renderSync (tpl, scope, opts) {
+    return toValue$1(this._render(tpl, scope, opts, true))
+  }
+
+  _parseAndRender (html, scope, opts, sync) {
+    const tpl = this.parse(html)
+    return this._render(tpl, scope, opts, sync)
+  }
+
+  async parseAndRender (html, scope, opts) {
+    return toPromise(this._parseAndRender(html, scope, opts, false))
+  }
+
+  parseAndRenderSync (html, scope, opts) {
+    return toValue$1(this._parseAndRender(html, scope, opts, true))
+  }
+
+  * _parseFile (file, opts, sync) {
+    const options = Object.assign({}, this.options, normalize(opts))
+    const paths = options.root.map(root => this.fs.resolve(root, file, options.extname))
+    if (this.fs.fallback !== undefined) {
+      const filepath = this.fs.fallback(file)
+      if (filepath !== undefined) { paths.push(filepath) }
+    }
+    for (const filepath of paths) {
+      const { cache } = options
+      if (cache) {
+        const tpls = yield cache.read(filepath)
+        if (tpls) { return tpls }
+      }
+      if (!(sync ? this.fs.existsSync(filepath) : yield this.fs.exists(filepath))) { continue }
+      const tpl = this.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath)
+      if (cache) { cache.write(filepath, tpl) }
+      return tpl
+    }
+    throw this.lookupError(file, options.root)
+  }
+
+  async parseFile (file, opts) {
+    return toPromise(this._parseFile(file, opts, false))
+  }
+
+  parseFileSync (file, opts) {
+    return toValue$1(this._parseFile(file, opts, true))
+  }
+
+  async renderFile (file, ctx, opts) {
+    const templates = await this.parseFile(file, opts)
+    return this.render(templates, ctx, opts)
+  }
+
+  renderFileSync (file, ctx, opts) {
+    const options = normalize(opts)
+    const templates = this.parseFileSync(file, options)
+    return this.renderSync(templates, ctx, opts)
+  }
+
+  _evalValue (str, ctx) {
+    const value = new Value(str, this.filters)
+    return value.value(ctx)
+  }
+
+  async evalValue (str, ctx) {
+    return toPromise(this._evalValue(str, ctx))
+  }
+
+  evalValueSync (str, ctx) {
+    return toValue$1(this._evalValue(str, ctx))
+  }
+
+  registerFilter (name, filter) {
+    this.filters.set(name, filter)
+  }
+
+  registerTag (name, tag) {
+    this.tags.set(name, tag)
+  }
+
+  plugin (plugin) {
+    return plugin.call(this, Liquid)
+  }
+
+  express () {
+        const self = this; // eslint-disable-line
+    return function (filePath, ctx, callback) {
+      const opts = { root: [...normalizeStringArray(this.root), ...self.options.root] }
+      self.renderFile(filePath, ctx, opts).then(html => callback(null, html), callback)
+    }
+  }
+
+  lookupError (file, roots) {
+    const err = new Error('ENOENT')
+    err.message = `ENOENT: Failed to lookup "${file}" in "${roots}"`
+    err.code = 'ENOENT'
+    return err
+  }
+
+  /**
+     * @deprecated use parseFile instead
+     */
+  async getTemplate (file, opts) {
+    return this.parseFile(file, opts)
+  }
+
+  /**
+     * @deprecated use parseFileSync instead
+     */
+  getTemplateSync (file, opts) {
+    return this.parseFileSync(file, opts)
+  }
+}
+
+export { AssertionError, Context, Drop, Emitter, Expression, Hash, Liquid, ParseError, ParseStream, TagToken, Token, TokenizationError, Tokenizer, typeGuards as TypeGuards, assert, evalQuotedToken, evalToken, isFalsy, isTruthy, toPromise, toThenable, toValue$1 as toValue }
diff --git a/test/fixtures/cache-tests/asset/marked.min.js b/test/fixtures/cache-tests/asset/marked.min.js
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/asset/marked.min.js
@@ -0,0 +1,6 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/markedjs/marked
+ */
+!function(e){"use strict";var k={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:f,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,nptable:f,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|<![A-Z][\\s\\S]*?>\\n*|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,table:f,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,text:/^[^\n]+/};function a(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=k.normal,this.options.pedantic?this.rules=k.pedantic:this.options.gfm&&(this.options.tables?this.rules=k.tables:this.rules=k.gfm)}k._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,k._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,k.def=i(k.def).replace("label",k._label).replace("title",k._title).getRegex(),k.bullet=/(?:[*+-]|\d{1,9}\.)/,k.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,k.item=i(k.item,"gm").replace(/bull/g,k.bullet).getRegex(),k.list=i(k.list).replace(/bull/g,k.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+k.def.source+")").getRegex(),k._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",k._comment=/<!--(?!-?>)[\s\S]*?-->/,k.html=i(k.html,"i").replace("comment",k._comment).replace("tag",k._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),k.paragraph=i(k.paragraph).replace("hr",k.hr).replace("heading",k.heading).replace("lheading",k.lheading).replace("tag",k._tag).getRegex(),k.blockquote=i(k.blockquote).replace("paragraph",k.paragraph).getRegex(),k.normal=d({},k),k.gfm=d({},k.normal,{fences:/^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),k.gfm.paragraph=i(k.paragraph).replace("(?!","(?!"+k.gfm.fences.source.replace("\\1","\\2")+"|"+k.list.source.replace("\\1","\\3")+"|").getRegex(),k.tables=d({},k.gfm,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),k.pedantic=d({},k.normal,{html:i("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",k._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/}),a.rules=k,a.lex=function(e,t){return new a(t).lex(e)},a.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g,"    ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},a.prototype.token=function(e,t){var n,r,s,i,l,o,a,h,p,u,c,g,f,d,m,b;for(e=e.replace(/^ +$/gm,"");e;)if((s=this.rules.newline.exec(e))&&(e=e.substring(s[0].length),1<s[0].length&&this.tokens.push({type:"space"})),s=this.rules.code.exec(e))e=e.substring(s[0].length),s=s[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?s:y(s,"\n")});else if(s=this.rules.fences.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"code",lang:s[2]?s[2].trim():s[2],text:s[3]||""});else if(s=this.rules.heading.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"heading",depth:s[1].length,text:s[2]});else if(t&&(s=this.rules.nptable.exec(e))&&(o={type:"table",header:x(s[1].replace(/^ *| *\| *$/g,"")),align:s[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:s[3]?s[3].replace(/\n$/,"").split("\n"):[]}).header.length===o.align.length){for(e=e.substring(s[0].length),c=0;c<o.align.length;c++)/^ *-+: *$/.test(o.align[c])?o.align[c]="right":/^ *:-+: *$/.test(o.align[c])?o.align[c]="center":/^ *:-+ *$/.test(o.align[c])?o.align[c]="left":o.align[c]=null;for(c=0;c<o.cells.length;c++)o.cells[c]=x(o.cells[c],o.header.length);this.tokens.push(o)}else if(s=this.rules.hr.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"hr"});else if(s=this.rules.blockquote.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"blockquote_start"}),s=s[0].replace(/^ *> ?/gm,""),this.token(s,t),this.tokens.push({type:"blockquote_end"});else if(s=this.rules.list.exec(e)){for(e=e.substring(s[0].length),a={type:"list_start",ordered:d=1<(i=s[2]).length,start:d?+i:"",loose:!1},this.tokens.push(a),n=!(h=[]),f=(s=s[0].match(this.rules.item)).length,c=0;c<f;c++)u=(o=s[c]).length,~(o=o.replace(/^ *([*+-]|\d+\.) */,"")).indexOf("\n ")&&(u-=o.length,o=this.options.pedantic?o.replace(/^ {1,4}/gm,""):o.replace(new RegExp("^ {1,"+u+"}","gm"),"")),c!==f-1&&(l=k.bullet.exec(s[c+1])[0],(1<i.length?1===l.length:1<l.length||this.options.smartLists&&l!==i)&&(e=s.slice(c+1).join("\n")+e,c=f-1)),r=n||/\n\n(?!\s*$)/.test(o),c!==f-1&&(n="\n"===o.charAt(o.length-1),r||(r=n)),r&&(a.loose=!0),b=void 0,(m=/^\[[ xX]\] /.test(o))&&(b=" "!==o[1],o=o.replace(/^\[[ xX]\] +/,"")),p={type:"list_item_start",task:m,checked:b,loose:r},h.push(p),this.tokens.push(p),this.token(o,!1),this.tokens.push({type:"list_item_end"});if(a.loose)for(f=h.length,c=0;c<f;c++)h[c].loose=!0;this.tokens.push({type:"list_end"})}else if(s=this.rules.html.exec(e))e=e.substring(s[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===s[1]||"script"===s[1]||"style"===s[1]),text:s[0]});else if(t&&(s=this.rules.def.exec(e)))e=e.substring(s[0].length),s[3]&&(s[3]=s[3].substring(1,s[3].length-1)),g=s[1].toLowerCase().replace(/\s+/g," "),this.tokens.links[g]||(this.tokens.links[g]={href:s[2],title:s[3]});else if(t&&(s=this.rules.table.exec(e))&&(o={type:"table",header:x(s[1].replace(/^ *| *\| *$/g,"")),align:s[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:s[3]?s[3].replace(/(?: *\| *)?\n$/,"").split("\n"):[]}).header.length===o.align.length){for(e=e.substring(s[0].length),c=0;c<o.align.length;c++)/^ *-+: *$/.test(o.align[c])?o.align[c]="right":/^ *:-+: *$/.test(o.align[c])?o.align[c]="center":/^ *:-+ *$/.test(o.align[c])?o.align[c]="left":o.align[c]=null;for(c=0;c<o.cells.length;c++)o.cells[c]=x(o.cells[c].replace(/^ *\| *| *\| *$/g,""),o.header.length);this.tokens.push(o)}else if(s=this.rules.lheading.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"heading",depth:"="===s[2]?1:2,text:s[1]});else if(t&&(s=this.rules.paragraph.exec(e)))e=e.substring(s[0].length),this.tokens.push({type:"paragraph",text:"\n"===s[1].charAt(s[1].length-1)?s[1].slice(0,-1):s[1]});else if(s=this.rules.text.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"text",text:s[0]});else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0));return this.tokens};var n={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:f,tag:"^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>",link:/^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:f,text:/^(`+|[^`])[\s\S]*?(?=[\\<!\[`*]|\b_| {2,}\n|$)/};function h(e,t){if(this.options=t||m.defaults,this.links=e,this.rules=n.normal,this.renderer=this.options.renderer||new r,this.renderer.options=this.options,!this.links)throw new Error("Tokens array requires a `links` property.");this.options.pedantic?this.rules=n.pedantic:this.options.gfm&&(this.options.breaks?this.rules=n.breaks:this.rules=n.gfm)}function r(e){this.options=e||m.defaults}function s(){}function p(e){this.tokens=[],this.token=null,this.options=e||m.defaults,this.options.renderer=this.options.renderer||new r,this.renderer=this.options.renderer,this.renderer.options=this.options,this.slugger=new t}function t(){this.seen={}}function u(e,t){if(t){if(u.escapeTest.test(e))return e.replace(u.escapeReplace,function(e){return u.replacements[e]})}else if(u.escapeTestNoEncode.test(e))return e.replace(u.escapeReplaceNoEncode,function(e){return u.replacements[e]});return e}function c(e){return e.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,function(e,t){return"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}function i(n,e){return n=n.source||n,e=e||"",{replace:function(e,t){return t=(t=t.source||t).replace(/(^|[^\[])\^/g,"$1"),n=n.replace(e,t),this},getRegex:function(){return new RegExp(n,e)}}}function l(e,t,n){if(e){try{var r=decodeURIComponent(c(n)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return null}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:")||0===r.indexOf("data:"))return null}t&&!g.test(n)&&(n=function(e,t){o[" "+e]||(/^[^:]+:\/*[^/]*$/.test(e)?o[" "+e]=e+"/":o[" "+e]=y(e,"/",!0));return e=o[" "+e],"//"===t.slice(0,2)?e.replace(/:[\s\S]*/,":")+t:"/"===t.charAt(0)?e.replace(/(:\/*[^/]*)[\s\S]*/,"$1")+t:e+t}(t,n));try{n=encodeURI(n).replace(/%25/g,"%")}catch(e){return null}return n}n._punctuation="!\"#$%&'()*+,\\-./:;<=>?@\\[^_{|}~",n.em=i(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=i(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=i(n.tag).replace("comment",k._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/,n._href=/\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=i(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=i(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:i(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:i(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:i(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:i(n.text).replace("]|","~]|").replace("|$","|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&'*+/=?^_`{\\|}~-]+@|$").getRegex()}),n.gfm.url=i(n.gfm.url,"i").replace("email",n.gfm._extended_email).getRegex(),n.breaks=d({},n.gfm,{br:i(n.br).replace("{2,}","*").getRegex(),text:i(n.gfm.text).replace("{2,}","*").getRegex()}),h.rules=n,h.output=function(e,t,n){return new h(t,n).output(e)},h.prototype.output=function(e){for(var t,n,r,s,i,l,o="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),o+=u(i[1]);else if(i=this.rules.tag.exec(e))!this.inLink&&/^<a /i.test(i[0])?this.inLink=!0:this.inLink&&/^<\/a>/i.test(i[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(i[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(i[0])&&(this.inRawBlock=!1),e=e.substring(i[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):u(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,r=i[2],this.options.pedantic?(t=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(r))?(r=t[1],s=t[3]):s="":s=i[3]?i[3].slice(1,-1):"",r=r.trim().replace(/^<([\s\S]*)>$/,"$1"),o+=this.outputLink(i,{href:h.escapes(r),title:h.escapes(s)}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),o+=this.renderer.strong(this.output(i[4]||i[3]||i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),o+=this.renderer.em(this.output(i[6]||i[5]||i[4]||i[3]||i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),o+=this.renderer.codespan(u(i[2].trim(),!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),o+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),o+=this.renderer.del(this.output(i[1]));else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),r="@"===i[2]?"mailto:"+(n=u(this.mangle(i[1]))):n=u(i[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.text.exec(e))e=e.substring(i[0].length),this.inRawBlock?o+=this.renderer.text(i[0]):o+=this.renderer.text(u(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===i[2])r="mailto:"+(n=u(i[0]));else{for(;l=i[0],i[0]=this.rules._backpedal.exec(i[0])[0],l!==i[0];);n=u(i[0]),r="www."===i[1]?"http://"+n:n}e=e.substring(i[0].length),o+=this.renderer.link(r,null,n)}return o},h.escapes=function(e){return e?e.replace(h.rules._escapes,"$1"):e},h.prototype.outputLink=function(e,t){var n=t.href,r=t.title?u(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,u(e[1]))},h.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},h.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,s=0;s<r;s++)t=e.charCodeAt(s),.5<Math.random()&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},r.prototype.code=function(e,t,n){var r=(t||"").match(/\S*/)[0];if(this.options.highlight){var s=this.options.highlight(e,r);null!=s&&s!==e&&(n=!0,e=s)}return r?'<pre><code class="'+this.options.langPrefix+u(r,!0)+'">'+(n?e:u(e,!0))+"</code></pre>\n":"<pre><code>"+(n?e:u(e,!0))+"</code></pre>"},r.prototype.blockquote=function(e){return"<blockquote>\n"+e+"</blockquote>\n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"<h"+t+' id="'+this.options.headerPrefix+r.slug(n)+'">'+e+"</h"+t+">\n":"<h"+t+">"+e+"</h"+t+">\n"},r.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"</"+r+">\n"},r.prototype.listitem=function(e){return"<li>"+e+"</li>\n"},r.prototype.checkbox=function(e){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox"'+(this.options.xhtml?" /":"")+"> "},r.prototype.paragraph=function(e){return"<p>"+e+"</p>\n"},r.prototype.table=function(e,t){return t&&(t="<tbody>"+t+"</tbody>"),"<table>\n<thead>\n"+e+"</thead>\n"+t+"</table>\n"},r.prototype.tablerow=function(e){return"<tr>\n"+e+"</tr>\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+"</"+n+">\n"},r.prototype.strong=function(e){return"<strong>"+e+"</strong>"},r.prototype.em=function(e){return"<em>"+e+"</em>"},r.prototype.codespan=function(e){return"<code>"+e+"</code>"},r.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"},r.prototype.del=function(e){return"<del>"+e+"</del>"},r.prototype.link=function(e,t,n){if(null===(e=l(this.options.sanitize,this.options.baseUrl,e)))return n;var r='<a href="'+u(e)+'"';return t&&(r+=' title="'+t+'"'),r+=">"+n+"</a>"},r.prototype.image=function(e,t,n){if(null===(e=l(this.options.sanitize,this.options.baseUrl,e)))return n;var r='<img src="'+e+'" alt="'+n+'"';return t&&(r+=' title="'+t+'"'),r+=this.options.xhtml?"/>":">"},r.prototype.text=function(e){return e},s.prototype.strong=s.prototype.em=s.prototype.codespan=s.prototype.del=s.prototype.text=function(e){return e},s.prototype.link=s.prototype.image=function(e,t,n){return""+n},s.prototype.br=function(){return""},p.parse=function(e,t){return new p(t).parse(e)},p.prototype.parse=function(e){this.inline=new h(e.links,this.options),this.inlineText=new h(e.links,d({},this.options,{renderer:new s})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},p.prototype.next=function(){return this.token=this.tokens.pop()},p.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},p.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},p.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,c(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,s="",i="";for(n="",e=0;e<this.token.header.length;e++)n+=this.renderer.tablecell(this.inline.output(this.token.header[e]),{header:!0,align:this.token.align[e]});for(s+=this.renderer.tablerow(n),e=0;e<this.token.cells.length;e++){for(t=this.token.cells[e],n="",r=0;r<t.length;r++)n+=this.renderer.tablecell(this.inline.output(t[r]),{header:!1,align:this.token.align[r]});i+=this.renderer.tablerow(n)}return this.renderer.table(s,i);case"blockquote_start":for(i="";"blockquote_end"!==this.next().type;)i+=this.tok();return this.renderer.blockquote(i);case"list_start":i="";for(var l=this.token.ordered,o=this.token.start;"list_end"!==this.next().type;)i+=this.tok();return this.renderer.list(i,l,o);case"list_item_start":i="";var a=this.token.loose;for(this.token.task&&(i+=this.renderer.checkbox(this.token.checked));"list_item_end"!==this.next().type;)i+=a||"text"!==this.token.type?this.tok():this.parseText();return this.renderer.listitem(i);case"html":return this.renderer.html(this.token.text);case"paragraph":return this.renderer.paragraph(this.inline.output(this.token.text));case"text":return this.renderer.paragraph(this.parseText());default:var h='Token with "'+this.token.type+'" type was not found.';if(!this.options.silent)throw new Error(h);console.log(h)}},t.prototype.slug=function(e){var t=e.toLowerCase().trim().replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},u.escapeTest=/[&<>"']/,u.escapeReplace=/[&<>"']/g,u.replacements={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},u.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,u.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var o={},g=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function f(){}function d(e){for(var t,n,r=1;r<arguments.length;r++)for(n in t=arguments[r])Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e}function x(e,t){var n=e.replace(/\|/g,function(e,t,n){for(var r=!1,s=t;0<=--s&&"\\"===n[s];)r=!r;return r?"|":" |"}).split(/ \|/),r=0;if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;r<n.length;r++)n[r]=n[r].trim().replace(/\\\|/g,"|");return n}function y(e,t,n){if(0===e.length)return"";for(var r=0;r<e.length;){var s=e.charAt(e.length-r-1);if(s!==t||n){if(s===t||!n)break;r++}else r++}return e.substr(0,e.length-r)}function m(e,n,r){if(null==e)throw new Error("marked(): input parameter is undefined or null");if("string"!=typeof e)throw new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected");if(r||"function"==typeof n){r||(r=n,n=null);var s,i,l=(n=d({},m.defaults,n||{})).highlight,t=0;try{s=a.lex(e,n)}catch(e){return r(e)}i=s.length;var o=function(t){if(t)return n.highlight=l,r(t);var e;try{e=p.parse(s,n)}catch(e){t=e}return n.highlight=l,t?r(t):r(null,e)};if(!l||l.length<3)return o();if(delete n.highlight,!i)return o();for(;t<s.length;t++)!function(n){"code"!==n.type?--i||o():l(n.text,n.lang,function(e,t){return e?o(e):null==t||t===n.text?--i||o():(n.text=t,n.escaped=!0,void(--i||o()))})}(s[t])}else try{return n&&(n=d({},m.defaults,n)),p.parse(a.lex(e,n),n)}catch(e){if(e.message+="\nPlease report this to https://github.com/markedjs/marked.",(n||m.defaults).silent)return"<p>An error occurred:</p><pre>"+u(e.message+"",!0)+"</pre>";throw e}}f.exec=f,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tables:!0,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=p,m.parser=p.parse,m.Renderer=r,m.TextRenderer=s,m.Lexer=a,m.lexer=a.lex,m.InlineLexer=h,m.inlineLexer=h.output,m.Slugger=t,m.parse=m,"undefined"!=typeof module&&"object"==typeof exports?module.exports=m:"function"==typeof define&&define.amd?define(function(){return m}):e.marked=m}(this||("undefined"!=typeof window?window:global));
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/asset/style.css b/test/fixtures/cache-tests/asset/style.css
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/asset/style.css
@@ -0,0 +1,201 @@
+html {
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 22px;
+}
+
+body {
+  margin: 2em 4em 10em 4em;
+}
+
+h1 {
+  line-height: 1.2em;
+}
+
+table {
+  border-spacing: 0;
+}
+
+th {
+  font-weight: normal;
+  padding: 0.2em 0.4em;
+}
+
+ul {
+  list-style-type: none;
+}
+
+ul#ToC {
+  column-count: 4;
+}
+
+th.category a {
+  color: white;
+}
+
+.category {
+  background-color: black;
+  color: white;
+  font-weight: bold;
+}
+
+.name {
+  margin: 0;
+  text-align: right;
+}
+
+.clickhint {
+  cursor: pointer;
+}
+
+.uuid {
+  color: #999;
+  margin-left: 0.5em;
+}
+
+.hint {
+  color: #999;
+  font-style: normal;
+}
+
+.shade {
+  background-color: #fafafa;
+}
+
+.description {
+  background-color: #eee;
+  padding: 3px 6px;
+}
+
+.warning {
+  padding: 0.5em;
+  margin: 0.5em auto;
+  background-color: #fde2e3;
+  border-left: solid 4px #f47477;
+}
+
+.key-item {
+  white-space: nowrap;
+}
+
+#download {
+  display: none;
+}
+
+#key {
+  background-color: white;
+  margin-bottom: 0;
+  padding: 10px;
+  border: 1px solid #999;
+}
+
+input.select {
+  margin-left: 6px;
+}
+
+code {
+  white-space: nowrap;
+}
+
+pre code {
+  white-space: pre;
+}
+
+@font-face {
+  font-family: 'FontAwesome';
+  src: url('/asset/fonts/fontawesome-webfont.eot?v=4.7.0');
+  src: url('/asset/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('/asset/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('/asset/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('/asset/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('/asset/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+.fa {
+  display: inline-block;
+  font: normal normal normal 18px/22px FontAwesome;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  margin: 0 10px;
+}
+
+.modal {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  width: 50vw;
+  height: 70vh;
+  transform: translate(-50%, -50%);
+  background-color: #fafafa;
+  opacity: 0;
+  visibility: hidden;
+  transition: all 0.3s ease;
+  align-items: center;
+  justify-content: center;
+  padding: 2em;
+  border: 2px solid #aaa;
+  border-radius: 0.5em;
+  overflow-y: auto;
+}
+.modal-open {
+  visibility: visible;
+  opacity: 1;
+  transition-delay: 0s;
+}
+
+.modal-exit {
+  position: absolute;
+  right: 15px;
+  top: 15px;
+  outline: none;
+  appearance: none;
+  color: red;
+  background: none;
+  border: 0px;
+  font-weight: bold;
+  font-size: 1.5em;
+  cursor: pointer;
+}
+
+.modal ul {
+  list-style-type: disc;
+}
+
+.modal pre {
+  background-color: #ddd;
+  padding: 0.5em 1em;
+  width: auto;
+}
+
+.noFrame {
+  display: none;
+}
+
+
+@page {
+  size: A4;
+  margin: 11mm 17mm 17mm 17mm;
+}
+
+@media screen {
+  #key {
+    position: fixed;
+    bottom: 0;
+    width: 80vw;
+    left: 10vw;
+    border-bottom: none;
+  }
+}
+
+@media print {
+  html, body {
+    width: 210mm;
+    height: 297mm;
+  }
+
+  #key {
+    width: 100%;
+  }
+
+  .noPrint * {
+    display: none;
+  }
+}
diff --git a/test/fixtures/cache-tests/docker/Dockerfile b/test/fixtures/cache-tests/docker/Dockerfile
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/Dockerfile
@@ -0,0 +1,89 @@
+FROM --platform=linux/amd64 ubuntu:devel
+LABEL maintainer="Mark Nottingham <mnot@mnot.net>"
+
+
+# package installs
+
+RUN apt-get update \
+    && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+    nodejs npm git openssh-client telnet \
+    squid \
+    nginx \
+    trafficserver \
+    apache2 \
+    varnish \
+    golang \
+    && rm -rf /var/lib/apt/lists/* \
+    && apt-get clean
+
+
+# squid
+
+COPY squid/squid.conf /etc/squid/conf.d/cache-test.conf
+
+ENV SQUID_CACHE_DIR=/var/spool/squid
+ENV SQUID_LOG_DIR=/var/log/squid
+ENV SQUID_USER=proxy
+EXPOSE 8001
+
+
+# nginx
+
+RUN mkdir /var/cache/nginx
+COPY nginx/nginx.conf /etc/nginx/sites-enabled/cache-test.conf
+EXPOSE 8002
+
+
+# trafficserver
+
+COPY trafficserver/ip_allow.config /etc/trafficserver/ip_allow.config
+COPY trafficserver/records.config /etc/trafficserver/records.config
+COPY trafficserver/remap.config /etc/trafficserver/remap.config
+
+ENV TS_CACHE_DIR=/var/run/trafficserver
+ENV TS_LOG_DIR=/var/log/trafficserver
+ENV TS_USER=trafficserver
+EXPOSE 8003
+
+
+# apache
+
+RUN a2enmod cache_socache
+RUN a2enmod cache_disk
+RUN a2enmod proxy_http
+
+COPY apache/ports.conf /etc/apache2/ports.conf
+COPY apache/apache.conf /etc/apache2/sites-enabled/cache-test.conf
+EXPOSE 8004
+
+
+# varnish
+
+EXPOSE 8005
+
+
+# caddy
+
+RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
+RUN ~/go/bin/xcaddy build --with github.com/caddyserver/cache-handler
+COPY caddy/Caddyfile /etc/caddy/Caddyfile
+EXPOSE 8006
+
+# setup
+
+COPY setup.sh /sbin/setup.sh
+RUN chmod 755 /sbin/setup.sh
+RUN /sbin/setup.sh
+
+# serve
+
+COPY serve.sh /sbin/serve.sh
+RUN chmod 755 /sbin/serve.sh
+
+
+# entrypoint
+
+COPY entrypoint.sh /sbin/entrypoint.sh
+RUN chmod 755 /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+CMD []
diff --git a/test/fixtures/cache-tests/docker/Makefile b/test/fixtures/cache-tests/docker/Makefile
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/Makefile
@@ -0,0 +1,11 @@
+
+PKGNAME=mnot/proxy-cache-tests
+
+
+.PHONY: build
+build:
+	docker build --pull -t $(PKGNAME) .
+
+.PHONY: upload
+upload:
+	docker push $(PKGNAME)
diff --git a/test/fixtures/cache-tests/docker/README.md b/test/fixtures/cache-tests/docker/README.md
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/README.md
@@ -0,0 +1,16 @@
+# Docker Image for Proxy Caches
+
+This Docker image runs reverse proxy caches for testing.
+
+To add a new reverse proxy:
+
+1. In `Dockerfile`:
+  1. add its ubuntu package to the `apt-get` line
+  2. make any configuration adjustments with `COPY` and/or `RUN` in a new section. In particular:
+     a. The proxy should listen on a dedicated port (the next in the 8000 series that's available)
+     b. It should use localhost:8000 for the origin server
+2. In `setup.sh`, run any additional configuration steps that are necessary
+3. In `entrypoint.sh`, add the needed commands (usually `sed`) to change the origin server hostname (for when the docker runs on desktop)
+4. In `serve.sh`, start the server in the background
+5. In `/test-docker.sh`, add the `PROXY_PORT` to the case statement
+
diff --git a/test/fixtures/cache-tests/docker/apache/apache.conf b/test/fixtures/cache-tests/docker/apache/apache.conf
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/apache/apache.conf
@@ -0,0 +1,20 @@
+Mutex sem
+
+<VirtualHost *:8004>
+
+  ProxyPass "/"  "http://localhost:8000/"
+  CacheEnable disk /
+
+  CacheRoot /var/cache/apache
+  CacheDirLevels 5
+  CacheDirLength 3
+
+  CacheDefaultExpire 0
+  CacheIgnoreNoLastMod On
+  CacheStoreExpired On
+  CacheNegotiatedDocs On
+  UseCanonicalName On
+  CacheHeader On
+  CacheDetailHeader on
+
+</VirtualHost>
diff --git a/test/fixtures/cache-tests/docker/apache/ports.conf b/test/fixtures/cache-tests/docker/apache/ports.conf
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/apache/ports.conf
@@ -0,0 +1 @@
+Listen 8004
diff --git a/test/fixtures/cache-tests/docker/caddy/Caddyfile b/test/fixtures/cache-tests/docker/caddy/Caddyfile
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/caddy/Caddyfile
@@ -0,0 +1,27 @@
+# The Caddyfile is an easy way to configure your Caddy web server.
+#
+# Unless the file starts with a global options block, the first
+# uncommented line is always the address of your site.
+#
+# To use your own domain name (with automatic HTTPS), first make
+# sure your domain's A/AAAA DNS records are properly pointed to
+# this machine's public IP, then replace ":80" below with your
+# domain name.
+
+{
+  cache
+}
+
+:8006 {
+  cache {
+    default_cache_control no-store
+  }
+  reverse_proxy http://127.0.0.1:8000 {
+  	transport http {
+  		dial_timeout 10s
+  	}
+  }
+}
+
+# Refer to the Caddy docs for more information:
+# https://caddyserver.com/docs/caddyfile
diff --git a/test/fixtures/cache-tests/docker/entrypoint.sh b/test/fixtures/cache-tests/docker/entrypoint.sh
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/entrypoint.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# If an argument is passed, make it the hostname for the origin server in proxy configs, and start the servers.
+
+if [[ ! -z $1 ]] ; then
+
+    # squid
+    sed -i s/localhost/$1/g /etc/squid/conf.d/cache-test.conf
+
+    # nginx
+    sed -i s/localhost/$1/g /etc/nginx/sites-enabled/cache-test.conf
+    sed -i s/worker_connections 768/worker_connections 2048/ /etc/nginx/nginx.conf
+
+    # trafficserver
+    sed -i s/localhost:8000/$1:8000/g /etc/trafficserver/remap.config
+
+    # apache
+    sed -i s/localhost/$1/g /etc/apache2/sites-enabled/cache-test.conf
+
+    # varnish
+    sed -i s/127.0.0.1/$1/ /etc/varnish/default.vcl
+
+    # caddy
+    sed -i s/127.0.0.1/$1/ /etc/caddy/Caddyfile
+
+    serve.sh
+  fi
+
+
+/bin/bash
+
diff --git a/test/fixtures/cache-tests/docker/nginx/nginx.conf b/test/fixtures/cache-tests/docker/nginx/nginx.conf
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/nginx/nginx.conf
@@ -0,0 +1,13 @@
+
+proxy_cache_path  /var/cache/nginx levels=1:2 keys_zone=my-cache:8m max_size=1000m inactive=600m;
+proxy_temp_path /var/cache/nginx/tmp;
+
+server {
+    listen       8002;
+
+    location / {
+        proxy_pass http://localhost:8000;
+        proxy_cache my-cache;
+        proxy_cache_revalidate on;
+    }
+}
diff --git a/test/fixtures/cache-tests/docker/serve.sh b/test/fixtures/cache-tests/docker/serve.sh
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/serve.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+echo "* Starting squid"
+squid -f /etc/squid/squid.conf -N &
+
+echo "* Starting nginx"
+/usr/sbin/nginx -g "daemon off;" &
+
+echo "* starting TrafficServer"
+/usr/bin/traffic_manager &
+
+echo "* Starting Apache"
+source /etc/apache2/envvars
+/usr/sbin/apache2 -X &
+
+echo "* Starting Varnish"
+/usr/sbin/varnishd -j unix -a 0.0.0.0:8005 -f /etc/varnish/default.vcl -p default_ttl=0 -p default_grace=0 -p default_keep=3600 -s malloc,64M
+
+echo "* Starting Caddy"
+HOME=/root /caddy run --config /etc/caddy/Caddyfile
diff --git a/test/fixtures/cache-tests/docker/setup.sh b/test/fixtures/cache-tests/docker/setup.sh
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/setup.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+
+## squid
+
+# Create log dir
+mkdir -p ${SQUID_LOG_DIR}
+chmod -R 755 ${SQUID_LOG_DIR}
+chown -R ${SQUID_USER}:${SQUID_USER} ${SQUID_LOG_DIR}
+
+# Create cache dir
+mkdir -p ${SQUID_CACHE_DIR}
+chown -R ${SQUID_USER}:${SQUID_USER} ${SQUID_CACHE_DIR}
+
+if [[ ! -d ${SQUID_CACHE_DIR}/00 ]]; then
+echo "Initializing cache..."
+$(which squid) -N -f /etc/squid/squid.conf -z 2&> /dev/null
+fi
+
+
+## apache
+
+mkdir /var/run/apache2
+mkdir /var/cache/apache
+chown -R www-data /var/cache/apache
+
+
+## trafficserver
+
+# Create log dir
+mkdir -p ${TS_LOG_DIR}
+chmod -R 755 ${TS_LOG_DIR}
+chown -R ${TS_USER}:${TS_USER} ${TS_LOG_DIR}
+
+# Create cache dir
+mkdir -p ${TS_CACHE_DIR}
+chown -R ${TS_USER}:${TS_USER} ${TS_CACHE_DIR}
+
+
+# varnish
+sed -i s/8080/8000/ /etc/varnish/default.vcl
diff --git a/test/fixtures/cache-tests/docker/squid/squid.conf b/test/fixtures/cache-tests/docker/squid/squid.conf
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/squid/squid.conf
@@ -0,0 +1,9 @@
+
+http_port 8001 accel defaultsite=localhost no-vhost
+cache_peer localhost parent 8000 0 no-query no-digest originserver default name=myAccel
+acl our_sites dstdomain localhost
+cache_peer_access myAccel allow all
+http_access allow all
+
+shutdown_lifetime 1 second
+connect_retries 3
diff --git a/test/fixtures/cache-tests/docker/trafficserver/ip_allow.config b/test/fixtures/cache-tests/docker/trafficserver/ip_allow.config
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/trafficserver/ip_allow.config
@@ -0,0 +1 @@
+src_ip=0.0.0.0-255.255.255.255 action=ip_allow
diff --git a/test/fixtures/cache-tests/docker/trafficserver/records.config b/test/fixtures/cache-tests/docker/trafficserver/records.config
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/trafficserver/records.config
@@ -0,0 +1,181 @@
+##############################################################################
+# *NOTE*: All options covered in this file should be documented in the docs:
+#
+#    https://docs.trafficserver.apache.org/records.config
+##############################################################################
+
+##############################################################################
+# Thread configurations. Docs:
+#    https://docs.trafficserver.apache.org/records.config#thread-variables
+##############################################################################
+CONFIG proxy.config.exec_thread.autoconfig INT 1
+CONFIG proxy.config.exec_thread.autoconfig.scale FLOAT 1.5
+CONFIG proxy.config.exec_thread.limit INT 2
+CONFIG proxy.config.accept_threads INT 1
+CONFIG proxy.config.task_threads INT 2
+CONFIG proxy.config.cache.threads_per_disk INT 8
+CONFIG proxy.config.exec_thread.affinity INT 1
+
+##############################################################################
+# Specify server addresses and ports to bind for HTTP and HTTPS. Docs:
+#    https://docs.trafficserver.apache.org/records.config#proxy.config.http.server_ports
+##############################################################################
+CONFIG proxy.config.http.server_ports STRING 8003
+
+##############################################################################
+# Via: headers. Docs:
+#     https://docs.trafficserver.apache.org/records.config#proxy-config-http-insert-response-via-str
+##############################################################################
+CONFIG proxy.config.http.insert_request_via_str INT 1
+CONFIG proxy.config.http.insert_response_via_str INT 0
+
+##############################################################################
+# Parent proxy configuration, in addition to these settings also see parent.config. Docs:
+#    https://docs.trafficserver.apache.org/records.config#parent-proxy-configuration
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/parent.config.en.html
+##############################################################################
+CONFIG proxy.config.http.parent_proxy_routing_enable INT 0
+CONFIG proxy.config.http.parent_proxy.retry_time INT 300
+CONFIG proxy.config.http.parent_proxy.connect_attempts_timeout INT 30
+CONFIG proxy.config.http.forward.proxy_auth_to_parent INT 0
+CONFIG proxy.config.http.uncacheable_requests_bypass_parent INT 1
+
+##############################################################################
+# HTTP connection timeouts (secs). Docs:
+#    https://docs.trafficserver.apache.org/records.config#http-connection-timeouts
+##############################################################################
+CONFIG proxy.config.http.keep_alive_no_activity_timeout_in INT 120
+CONFIG proxy.config.http.keep_alive_no_activity_timeout_out INT 120
+CONFIG proxy.config.http.transaction_no_activity_timeout_in INT 30
+CONFIG proxy.config.http.transaction_no_activity_timeout_out INT 30
+CONFIG proxy.config.http.transaction_active_timeout_in INT 900
+CONFIG proxy.config.http.transaction_active_timeout_out INT 0
+CONFIG proxy.config.http.accept_no_activity_timeout INT 120
+CONFIG proxy.config.net.default_inactivity_timeout INT 86400
+
+##############################################################################
+# Origin server connect attempts. Docs:
+#    https://docs.trafficserver.apache.org/records.config#origin-server-connect-attempts
+##############################################################################
+CONFIG proxy.config.http.connect_attempts_max_retries INT 3
+CONFIG proxy.config.http.connect_attempts_max_retries_dead_server INT 1
+CONFIG proxy.config.http.connect_attempts_rr_retries INT 3
+CONFIG proxy.config.http.connect_attempts_timeout INT 30
+CONFIG proxy.config.http.post_connect_attempts_timeout INT 1800
+CONFIG proxy.config.http.down_server.cache_time INT 60
+CONFIG proxy.config.http.down_server.abort_threshold INT 10
+
+##############################################################################
+# Negative response caching, for redirects and errors. Docs:
+#    https://docs.trafficserver.apache.org/records.config#negative-response-caching
+##############################################################################
+CONFIG proxy.config.http.negative_caching_enabled INT 0
+CONFIG proxy.config.http.negative_caching_lifetime INT 1800
+
+##############################################################################
+# Proxy users variables. Docs:
+#    https://docs.trafficserver.apache.org/records.config#proxy-user-variables
+##############################################################################
+CONFIG proxy.config.http.insert_client_ip INT 1
+CONFIG proxy.config.http.insert_squid_x_forwarded_for INT 1
+
+##############################################################################
+# Security. Docs:
+#    https://docs.trafficserver.apache.org/records.config#security
+##############################################################################
+CONFIG proxy.config.http.push_method_enabled INT 0
+
+##############################################################################
+# Enable / disable HTTP caching. Useful for testing, but also as an
+# overridable (per remap) config
+##############################################################################
+CONFIG proxy.config.http.cache.http INT 1
+
+##############################################################################
+# Cache control. Docs:
+#    https://docs.trafficserver.apache.org/records.config#cache-control
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/cache.config.en.html
+##############################################################################
+CONFIG proxy.config.http.cache.ignore_client_cc_max_age INT 1
+CONFIG proxy.config.http.normalize_ae INT 1
+CONFIG proxy.config.http.cache.cache_responses_to_cookies INT 1
+CONFIG proxy.config.http.cache.cache_urls_that_look_dynamic INT 1
+    # https://docs.trafficserver.apache.org/records.config#proxy-config-http-cache-when-to-revalidate
+CONFIG proxy.config.http.cache.when_to_revalidate INT 0
+    # https://docs.trafficserver.apache.org/records.config#proxy-config-http-cache-required-headers
+CONFIG proxy.config.http.cache.required_headers INT 2
+
+##############################################################################
+# Heuristic cache expiration. Docs:
+#    https://docs.trafficserver.apache.org/records.config#heuristic-expiration
+##############################################################################
+CONFIG proxy.config.http.cache.heuristic_min_lifetime INT 3600
+CONFIG proxy.config.http.cache.heuristic_max_lifetime INT 86400
+CONFIG proxy.config.http.cache.heuristic_lm_factor FLOAT 0.10
+
+##############################################################################
+# Network. Docs:
+#    https://docs.trafficserver.apache.org/records.config#network
+##############################################################################
+CONFIG proxy.config.net.connections_throttle INT 30000
+CONFIG proxy.config.net.max_connections_in INT 30000
+CONFIG proxy.config.net.max_connections_active_in INT 10000
+
+##############################################################################
+# RAM and disk cache configurations. Docs:
+#    https://docs.trafficserver.apache.org/records.config#ram-cache
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/storage.config.en.html
+##############################################################################
+CONFIG proxy.config.cache.ram_cache.size INT -1
+CONFIG proxy.config.cache.ram_cache_cutoff INT 4194304
+    # https://docs.trafficserver.apache.org/records.config#proxy-config-cache-limits-http-max-alts
+CONFIG proxy.config.cache.limits.http.max_alts INT 5
+    # https://docs.trafficserver.apache.org/records.config#proxy-config-cache-max-doc-size
+CONFIG proxy.config.cache.max_doc_size INT 0
+CONFIG proxy.config.cache.min_average_object_size INT 8000
+
+##############################################################################
+# Logging Config. Docs:
+#    https://docs.trafficserver.apache.org/records.config#logging-configuration
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/logging.yaml.en.html
+##############################################################################
+CONFIG proxy.config.log.logging_enabled INT 3
+CONFIG proxy.config.log.max_space_mb_for_logs INT 25000
+CONFIG proxy.config.log.max_space_mb_headroom INT 1000
+CONFIG proxy.config.log.rolling_enabled INT 1
+CONFIG proxy.config.log.rolling_interval_sec INT 86400
+CONFIG proxy.config.log.rolling_size_mb INT 10
+CONFIG proxy.config.log.auto_delete_rolled_files INT 1
+CONFIG proxy.config.log.periodic_tasks_interval INT 5
+
+##############################################################################
+# These settings control remapping, and if the proxy allows (open) forward proxy or not. Docs:
+#    https://docs.trafficserver.apache.org/records.config#url-remap-rules
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/remap.config.en.html
+##############################################################################
+CONFIG proxy.config.url_remap.remap_required INT 1
+    # https://docs.trafficserver.apache.org/records.config#proxy-config-url-remap-pristine-host-hdr
+CONFIG proxy.config.url_remap.pristine_host_hdr INT 1
+    # https://docs.trafficserver.apache.org/records.config#reverse-proxy
+CONFIG proxy.config.reverse_proxy.enabled INT 1
+
+##############################################################################
+# SSL Termination. Docs:
+#    https://docs.trafficserver.apache.org/records.config#client-related-configuration
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/ssl_multicert.config.en.html
+##############################################################################
+CONFIG proxy.config.ssl.client.verify.server INT 0
+CONFIG proxy.config.ssl.client.CA.cert.filename STRING NULL
+CONFIG proxy.config.ssl.server.cipher_suite STRING ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
+
+##############################################################################
+# Debugging. Docs:
+#    https://docs.trafficserver.apache.org/records.config#diagnostic-logging-configuration
+##############################################################################
+CONFIG proxy.config.diags.debug.enabled INT 0
+CONFIG proxy.config.diags.debug.tags STRING http|dns
+# ToDo: Undocumented
+CONFIG proxy.config.dump_mem_info_frequency INT 0
+CONFIG proxy.config.http.slow.log.threshold INT 0
+
+CONFIG proxy.config.admin.user_id STRING trafficserver
diff --git a/test/fixtures/cache-tests/docker/trafficserver/remap.config b/test/fixtures/cache-tests/docker/trafficserver/remap.config
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/docker/trafficserver/remap.config
@@ -0,0 +1,197 @@
+#
+# remap.config - URL Remapping Config File
+#
+# Documentation:
+#    https://docs.trafficserver.apache.org/en/latest/admin-guide/files/remap.config.en.html
+#
+# Using remap.config allows you to accomplish two things:
+#
+#  1) Rewrite a URL (from the client) before sending it to the Origin Server.
+#  2) Protect the proxy server, to only allow certain requests.
+#
+#  With the default configurations, at least one remap rule is required. This
+#  can be relaxed with the following configuration in records.config:
+#
+#   CONFIG proxy.config.url_remap.remap_required INT 0
+#
+# Be aware, doing so makes the proxy a generic, open-relay!
+#
+#  The format is:
+#      <map_type> client-URL origin-server-URL <tag_value> <filtering>
+#
+# Where client-URL and origin-server-URL are both of the format
+#    <scheme>://<host>:<port>/<path_prefix>
+#
+#  The <tag_value> directive is optional and can be different for different
+#  types of <map_type>. The <filtering arguments> are optional ACL-like
+#  arguments unique for each remap rule
+#
+#  Six different types of mappings are possible:
+#     map
+#     map_with_referer
+#     map_with_recv_port
+#     reverse_map
+#     redirect
+#     redirect_temporary
+#
+#  Each of these map types can be prefixed with the string 'regex_' to indicate
+#  that the rule will have regular expression strings. See the last part of
+#  this description for more information on regex support.
+#
+#  The 'map' mapping is the most straightforward.  Requests that match the
+#  client-URL are rewritten into the origin-server-URL. The user agent will see
+#  the page on the remapped URL, but will not be notified of the address
+#  change.
+#
+#  The 'map_with_referer' is an extended version of 'map', which can be used to
+#  activate the so-called "deep linking protection" feature available in
+#  Apache Traffic Server.
+#
+#  The 'map_with_recv_port' is exactly like 'map' except that it uses the port
+#  at which the request was received to perform the mapping instead of the port
+#  present in the request. When present, 'map_with_recv_port' mappings are
+#  checked first. If there is a match, then it is chosen without evaluating the
+#  "regular" forward mapping rules.
+#
+#  The 'reverse_map' mapping is used to rewrite location headers sent by the
+#  origin server.  The 'redirect' mapping creates a permanent redirect message
+#  and informs the browser of the URL change.
+#
+#  The 'redirect_temporary' mapping acts in the same way but tells the browser
+#  that this redirect is only temporary.  We need to map the URL in reverse
+#  proxy mode so that user agents know to contact Traffic Server and not
+#  attempt to contact the Origin Server directly.
+#
+#  For example, you can set up a reverse proxy for www.example.com with the
+#  real content situated at server1.example.com with the rules:
+#
+#    map          http://www.example.com/      http://server1.example.com/
+#    reverse_map  http://server1.example.com/  http://www.example.com/
+#
+#  Or you could permanently redirect users trying to access www.oldserver.com
+#  to www.newserver.com with the following rule:
+#
+#    redirect	http://www.oldserver.com/    http://www.newserver.com
+#
+#  If the redirect is only temporary, you want to only temporarily remap the
+#  URL.  You could use the following rule to divert users away from a failed
+#  server:
+#
+#  redirect_temporary http://broken.firm.com http://working.firm.com
+#
+#  In order to use "deep linking protection" Traffic Server's feature, the
+#  'map_with_referer' mapping scheme must be used. In general, the format of is
+#  the following:
+#
+#     map_with_referer client-URL origin-server-URL redirect-URL regex1 [regex2 ...]
+#
+#  'redirect-URL' is a redirection URL specified according to RFC 2616 and can
+#  contain special formatting instructions for run-time modifications of the
+#  resulting redirection URL.  All regexes Perl compatible  regular expressions,
+#  which describes the content of the "Referer" header which must be
+#  verified. In case an actual request does not have "Referer" header or it
+#  does not match with referer regular expression, the HTTP request will be
+#  redirected to 'redirect-URL'.
+#
+#  At least one regular expressions must be specified in order to activate
+#  'deep linking protection'.  There are limitations for the number of referer
+#  regular expression strings - 2048.  In order to enable the 'deep linking
+#  protection' feature in Traffic Server, configure records.config with:
+#
+#    CONFIG proxy.config.http.referer_filter INT 1
+#
+#  In order to enable run-time formatting for redirect0URL, configure
+#
+#    CONFIG proxy.config.http.referer_format_redirect INT 1
+#
+#  When run-time formatting for redirect-URL was enabled the following format
+#  symbols can be used:
+#
+#    %r - to substitute original "Referer" header string
+#    %f - to substitute client-URL from 'map_with_referer' record
+#    %t - to substitute origin-server-URL from 'map_with_referer' record
+#    %o - to substitute request URL to origin server, which was created a
+#         the result of a mapping operation
+#
+#  Note: There is a special referer type "~*" that can be used in order to
+#  specify that the Referer header is optional in the request.  If "~*" referer
+#  was used in map_with_referer mapping, only requests with Referer header will
+#  be verified for validity.  If the "~" symbol was specified before referer
+#  regular expression, it means that the request with a matching referer header
+#  will be redirected to redirectURL. It can be used to create a so-called
+#  negative referer list.  If "*" was used as a referer regular expression -
+#  all referers are allowed.  Various combinations of "*" and "~" in a referer
+#  list can be used to create different filtering rules.
+#
+#  Examples:
+#    map_with_referer http://y.foo.bar.com/x/yy/  http://foo.bar.com/x/yy/ http://games.bar.com/new_games .*\.bar\.com www.bar-friends.com
+#
+#  Explanation: Referer header must be in the request, only ".*\.bar\.com"
+#  and "www.bar-friends.com" are allowed.
+#
+#    map_with_referer http://y.foo.bar.com/x/yy/  http://foo.bar.com/x/yy/ http://games.bar.com/new_games * ~.*\.evil\.com
+#
+#  Explanation: Referer header must be in the request but all referers are
+#  allowed except ".*\.evil\.com".
+#
+#    map_with_referer http://y.foo.bar.com/x/yy/  http://foo.bar.com/x/yy/ http://games.bar.com/error ~* * ~.*\.evil\.com
+#
+#  Explanation: Referer header is optional. However, if Referer header exists,
+#  only request from ".*\.evil\.com" will be redirected to redirect-URL.
+#
+#  There are optional filtering arguments that can be specified at the end of the mapping definition line:
+#
+#    @action=allow|deny
+#    @src_ip=IP-address
+#    @method=HTTP method string (CONNECT|DELETE|GET|HEAD|OPTIONS|POST|PURGE|PUT|TRACE|PUSH)
+#    @plugin=<plugin_path>
+#    @pparam=<plugin_param>
+#
+#  There is no limitation for the number of filtering arguments.
+#
+#  Example:
+#    map http://foo.cow.com/ http://bar.cow.com @src_ip=10.72.118.51-10.72.118.62 @method=GET @method=DELETE @src_ip=192.168.0.1-192.168.0.254 @action=allow @method=PUT
+#
+#  Traffic Server supports WebSockets but it must be enabled via remap. WebSocket upgrades are automatically
+#  detected when there exists a remap rule containing a ws:// scheme.
+#
+#  Example:
+#     map ws://bar.com/ ws://foo.com/
+#
+#  Explanation: When a request comes in with the appropriate upgrade headers, Traffic Server will use this
+#   remap rule in an attempt to establish and maintain a websocket connection.
+#
+#  Named filters can be created and applied to blocks of mappings
+#  using the .definefilter, .activatefilter, and .deactivatefilter
+#  directives. Named filters must be defined using .definefilter
+#  before being used. Once defined, .activatefilter can used to
+#  activate a filter for all mappings that follow until deactivated
+#  with .deactivatefilter.
+#
+#  Example:
+#    .definefilter disable_delete_purge @action=deny @method=delete @method=purge
+#    .definefilter internal_only @action=allow @src_ip=192.168.0.1-192.168.0.254 @src_ip=10.0.0.1-10.0.0.254
+#
+#    .activatefilter disable_delete_purge
+#
+#    map http://foo.example.com/ http://bar.example.com/
+#
+#    .activatefilter internal_only
+#    map http://www.example.com/admin http://internal.example.com/admin
+#    .deactivatefilter internal_only
+#
+#    map http://www.example.com/ http://internal.example.com/
+#
+#
+#  Regex support: Regular expressions can be specified in the rules with the
+#  following limitations:
+#
+#  1) Only the host field can have regexes - the scheme, port and other
+#     fields cannot.
+#  2) The number of capturing sub-patterns is limited to 9;
+#     this means $0 through $9 can be used as substitution place holders ($0
+#     will be the entire input string)
+#  3) The number of substitutions in the expansion string is limited to 10.
+#
+
+map http://localhost:8003/ http://localhost:8000/
diff --git a/test/fixtures/cache-tests/index.html b/test/fixtures/cache-tests/index.html
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/index.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>HTTP Caching Tests</title>
+    <link rel="stylesheet" href="/asset/style.css" type="text/css">
+    <meta property="og:type" content="website">
+    <meta property="og:title" content="HTTP Caching Tests">
+    <meta property="og:description" content="Finding out how browser and proxy caches behave (and misbehave)">
+    <meta property="og:url" content="https://cache-tests.fyi/">
+    <meta property="og:site_name" content="HTTP Caching Tests">
+    <meta property="og:image" content="https://cache-tests.fyi/asset/badge.png">
+    <link rel="preload" as="fetch" crossorigin href="/test-engine/lib/tpl/explain-test.liquid">
+    <link rel="preload" as="fetch" crossorigin href="/test-engine/lib/tpl/checks.liquid">
+    <link rel="preload" as="fetch" crossorigin href="/test-engine/lib/tpl/header-list.liquid">
+    <link rel="preload" as="fetch" crossorigin href="/test-engine/lib/tpl/header-magic.liquid">
+    <script type="module">
+      import index from './results/index.mjs'
+      import * as summary from './test-engine/lib/summary.mjs'
+      import * as display from './test-engine/lib/display.mjs'
+      import baseTests from './tests/index.mjs'
+
+      const loc = new URL(window.location)
+      const suiteIds = loc.searchParams.getAll('suite')
+      const testIds = loc.searchParams.getAll('id')
+      const isFrame = loc.searchParams.get('frame') && true || false
+      const isDefault = testIds.length === 0 && suiteIds.length === 0
+
+      if (! isFrame) {
+        document.querySelectorAll('.noFrame').forEach(e => {e.style.display = 'block'})
+      }
+
+      const ToC = document.getElementById('ToC')
+      const target = document.getElementById('target')
+      const key = document.getElementById('key')
+
+      display.showKey(key)
+      summary.loadResults(index)
+      .then(results => {
+        if (isDefault) {
+          summary.showToC(ToC, baseTests)
+        }
+        summary.showResults(target, baseTests, results, testIds, suiteIds)
+      })
+      .then(() => {
+        if (window.location.hash !== "") {
+          const fragId = window.location.hash.substring(1)
+          document.getElementById(fragId).scrollIntoView()
+        }
+        if (! isFrame) {
+          summary.selectClickListen()
+        }
+      })
+    </script>
+  </head>
+  <body>
+    <h1 class='noFrame'>HTTP Caching Tests</h1>
+
+    <p class="warning noFrame">These tests are a work in progress. The reported results may be
+    faulty, and do not necessarily reflect the true capabilities of each cache. They should not be
+    used evaluate or compare feature support.
+    <strong>This is an open source project</strong>; to make contributions, add your
+    implementation's results, file issues or learn more, see <a
+    href="https://github.com/http-tests/cache-tests">the repository</a>.</p>
+
+    <p class='noFrame noPrint'><span>See also </span><a href="/spec/rfc9111.html">test results interspersed with the current specification text</a>.</p>
+
+    <p id="key">
+      <span class="key-item"><span class="fa" data-kind="pass"></span>passed &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="optional_fail"></span>optional test failed &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="fail"></span>conformance test failed &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="yes"></span> / <span class="fa" data-kind="no"></span>behaviour check results &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="harness_fail"></span>test harness failure &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="setup_fail"></span>test failed during setup &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="retry"></span>cache retries a request &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="dependency_fail"></span>test dependency failed &nbsp;</span>
+      <span class="key-item"><span class="fa" data-kind="untested"></span>not tested</span>
+    </p>
+
+    <ul id="ToC" class="noFrame noPrint">
+    </ul>
+
+    <form method="GET" id="selectForm">
+
+      <p class="noFrame noPrint"><i>
+        Click on test names for details of what is sent and checked. Hover over failed tests for the reason why they failed.
+      </i></p>
+
+      <p class="noFrame noPrint">
+        <a href="#" id="select">Select test results to display</a>
+      </p>
+
+      <table id="target">
+      </table>
+    </form>
+  </body>
+</html>
diff --git a/test/fixtures/cache-tests/package.json b/test/fixtures/cache-tests/package.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/package.json
@@ -0,0 +1,42 @@
+{
+  "name": "http-cache-tests",
+  "version": "0.4.5",
+  "description": "Tests for HTTP caches",
+  "homepage": "https://cache-tests.fyi/",
+  "author": "Mark Nottingham",
+  "license": "BSD-3-Clause",
+  "engines": {
+    "node": ">=14.8.0"
+  },
+  "dependencies": {
+    "liquidjs": "^10.9.2",
+    "marked": "^15.0.0",
+    "node-fetch": "3.1.1",
+    "node-fetch-with-proxy": "^0.1.6",
+    "npm": "^10.1.0"
+  },
+  "scripts": {
+    "server": "node test-engine/server/server.mjs",
+    "cli": "node --no-warnings test-engine/cli.mjs",
+    "export": "node --no-warnings test-engine/export.mjs",
+    "validate": "node --no-warnings test-engine/export.mjs validate",
+    "lint": "standard test-engine tests spec",
+    "fix": "standard --fix test-engine tests spec"
+  },
+  "config": {
+    "protocol": "http",
+    "port": "8000",
+    "base": "",
+    "id": "",
+    "pidfile": "/tmp/http-cache-test-server.pid"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/http-tests/cache-tests.git"
+  },
+  "devDependencies": {
+    "ajv": "^8.12.0",
+    "font-awesome": "^4.7.0",
+    "standard": "^17.1.0"
+  }
+}
diff --git a/test/fixtures/cache-tests/results/apache.json b/test/fixtures/cache-tests/results/apache.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/apache.json
@@ -0,0 +1,675 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": true,
+  "304-etag-update-response-Content-Encoding": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-Content-Foo": true,
+  "304-etag-update-response-Content-Length": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-Content-Location": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-ETag": [
+    "Setup",
+    "retry"
+  ],
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": true,
+  "304-etag-update-response-Set-Cookie2": true,
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": true,
+  "304-etag-update-response-X-Frame-Options": true,
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": true,
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": true,
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-suffix-twoline": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": true,
+  "cc-resp-no-cache-revalidate-fresh": true,
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": true,
+  "ccreq-ma1": true,
+  "ccreq-magreaterage": true,
+  "ccreq-max-stale": true,
+  "ccreq-max-stale-age": true,
+  "ccreq-min-fresh": true,
+  "ccreq-min-fresh-age": true,
+  "ccreq-no-cache": true,
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "ccreq-no-store": true,
+  "ccreq-oic": true,
+  "cdn-cc-invalid-sh-type-unknown": true,
+  "cdn-cc-invalid-sh-type-wrong": true,
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 01:05:30 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-age": true,
+  "cdn-max-age-case-insensitive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-cc-max-age-invalid-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-extension": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-long-cc-max-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-private": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-remove-age-exceed": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "cdn-remove-header": true,
+  "conditional-304-etag": true,
+  "conditional-etag-forward": true,
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": true,
+  "conditional-etag-quoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": true,
+  "conditional-etag-strong-respond-multiple-last": true,
+  "conditional-etag-strong-respond-multiple-second": true,
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-unquoted-respond-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-unquoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": true,
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-weak-respond-lowercase": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-weak-respond-omit-slash": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-lm-fresh": true,
+  "conditional-lm-fresh-earlier": true,
+  "conditional-lm-fresh-no-lm": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-lm-fresh-rfc850": true,
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": true,
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": true,
+  "freshness-expires-invalid-2-digit-year": true,
+  "freshness-expires-invalid-aest": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": true,
+  "freshness-expires-invalid-multiple-lines": true,
+  "freshness-expires-invalid-multiple-spaces": true,
+  "freshness-expires-invalid-no-comma": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-tz": true,
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-decimal-zero": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": true,
+  "freshness-max-age-space-before-equals": true,
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-fresh-stale-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sameline": true,
+  "freshness-max-age-two-stale-fresh-sepline": true,
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-writethrough": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": true,
+  "headers-omit-headers-listed-in-Connection": true,
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": true,
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": true,
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": true,
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": true,
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Setup",
+    "Response 2 status is 500, not 599"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": true,
+  "heuristic-delta-1800": true,
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": true,
+  "heuristic-delta-3600": true,
+  "heuristic-delta-43200": true,
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": true,
+  "heuristic-delta-600": true,
+  "heuristic-delta-86400": true,
+  "invalidate-DELETE": true,
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": true,
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": true,
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": true,
+  "other-age-update-expires": true,
+  "other-age-update-max-age": true,
+  "other-authorization": true,
+  "other-authorization-must-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-authorization-public": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-authorization-smaxage": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-cookie": true,
+  "other-date-update": [
+    "Assertion",
+    "Response 2 header Date is \"Tue, 09 Jul 2024 01:05:24 GMT\", not \"Tue, 09 Jul 2024 01:05:21 GMT\""
+  ],
+  "other-date-update-expires": [
+    "Assertion",
+    "Response 2 header Date is \"Tue, 09 Jul 2024 01:05:24 GMT\", not \"Tue, 09 Jul 2024 01:05:21 GMT\""
+  ],
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": true,
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": true,
+  "partial-store-complete-reuse-partial-no-last": true,
+  "partial-store-complete-reuse-partial-suffix": true,
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": true,
+  "partial-use-stored-headers": true,
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": true,
+  "stale-close": true,
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": true,
+  "stale-sie-close": true,
+  "stale-warning-become": true,
+  "stale-warning-stored": true,
+  "stale-while-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate-window": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Setup",
+    "Response 2 status is 500, not 299"
+  ],
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": true,
+  "status-302-stale": true,
+  "status-303-fresh": true,
+  "status-303-stale": true,
+  "status-307-fresh": true,
+  "status-307-stale": true,
+  "status-308-fresh": true,
+  "status-308-stale": true,
+  "status-400-fresh": true,
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Setup",
+    "Response 2 status is 500, not 499"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": true,
+  "status-500-stale": true,
+  "status-502-fresh": true,
+  "status-502-stale": true,
+  "status-503-fresh": true,
+  "status-503-stale": true,
+  "status-504-fresh": true,
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Setup",
+    "Response 2 status is 500, not 599"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": true,
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
diff --git a/test/fixtures/cache-tests/results/caddy.json b/test/fixtures/cache-tests/results/caddy.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/caddy.json
@@ -0,0 +1,843 @@
+{
+  "304-etag-update-response-Cache-Control": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Clear-Site-Data": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Encoding": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Foo": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Length": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Location": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Security-Policy": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Content-Type": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-ETag": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Expires": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Public-Key-Pins": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Set-Cookie": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Set-Cookie2": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Test-Header": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-X-Content-Foo": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-X-Frame-Options": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-X-Test-Header": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-X-XSS-Protection": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-lm-use-stored-Test-Header": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": true,
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-suffix-twoline": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": [
+    "Assertion",
+    "Request 3 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "cc-resp-no-cache-revalidate-fresh": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-max-stale-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-min-fresh": true,
+  "ccreq-min-fresh-age": true,
+  "ccreq-no-cache": true,
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": [
+    "Assertion",
+    "Response 1 status is 200, not 504"
+  ],
+  "cdn-cc-invalid-sh-type-unknown": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-cc-invalid-sh-type-wrong": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 01:03:11 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": true,
+  "cdn-max-age": true,
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": true,
+  "cdn-max-age-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-case-insensitive": true,
+  "cdn-max-age-cc-max-age-invalid-expires": true,
+  "cdn-max-age-expires": true,
+  "cdn-max-age-extension": true,
+  "cdn-max-age-long-cc-max-age": true,
+  "cdn-max-age-max": true,
+  "cdn-max-age-max-plus": true,
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": true,
+  "cdn-private": true,
+  "cdn-remove-age-exceed": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "cdn-remove-header": true,
+  "conditional-304-etag": true,
+  "conditional-etag-forward": true,
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": true,
+  "conditional-etag-quoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-strong-generate": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-strong-respond-multiple-last": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-strong-respond-multiple-second": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-unquoted-respond-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-etag-unquoted-respond-unquoted": true,
+  "conditional-etag-vary-headers": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": true,
+  "conditional-etag-weak-respond-lowercase": true,
+  "conditional-etag-weak-respond-omit-slash": true,
+  "conditional-lm-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-lm-fresh-earlier": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-lm-fresh-no-lm": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-lm-fresh-rfc850": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-age-fast-date": true,
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-far-future": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-future": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": true,
+  "freshness-expires-invalid-2-digit-year": true,
+  "freshness-expires-invalid-aest": true,
+  "freshness-expires-invalid-date": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-invalid-date-dashes": true,
+  "freshness-expires-invalid-multiple-lines": true,
+  "freshness-expires-invalid-multiple-spaces": true,
+  "freshness-expires-invalid-no-comma": true,
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": true,
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-month": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-tz": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-weekday": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-decimal-zero": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": true,
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": true,
+  "freshness-max-age-space-before-equals": true,
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-fresh-stale-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sameline": true,
+  "freshness-max-age-two-stale-fresh-sepline": true,
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "FetchError",
+    "request to http://localhost:8006/test/3a29d44d-d103-492b-9046-be67546d71d7 failed, reason: Parse Error: Empty Content-Length"
+  ],
+  "head-200-retain": [
+    "FetchError",
+    "request to http://localhost:8006/test/d07b2651-4270-4ea6-ae9f-041b1b82aae8 failed, reason: Parse Error: Empty Content-Length"
+  ],
+  "head-200-update": [
+    "FetchError",
+    "request to http://localhost:8006/test/eb317c8b-cea4-4b4c-b72b-71df1aa6e863 failed, reason: Parse Error: Empty Content-Length"
+  ],
+  "head-410-update": [
+    "FetchError",
+    "request to http://localhost:8006/test/7f947275-bffe-4c72-a493-c64352423d8e failed, reason: Parse Error: Empty Content-Length"
+  ],
+  "head-writethrough": [
+    "FetchError",
+    "request to http://localhost:8006/test/f9b80f6e-7980-4a44-ae58-d58ac539223c failed, reason: Parse Error: Empty Content-Length"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Connection": true,
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": true,
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": [
+    "Setup",
+    "Response 1 status is 502, not 200"
+  ],
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1800": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-3600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-43200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-86400": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "invalidate-DELETE": true,
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": true,
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": true,
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": true,
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": true,
+  "other-age-update-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-update-max-age": true,
+  "other-authorization": true,
+  "other-authorization-must-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-authorization-public": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-authorization-smaxage": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-cookie": true,
+  "other-date-update": true,
+  "other-date-update-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-date-update-expires-update": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": [
+    "Assertion",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-store-complete-reuse-partial-no-last": [
+    "Assertion",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-store-complete-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-store-partial-complete": [
+    "Setup",
+    "Response 2 status is 206, not 200"
+  ],
+  "partial-store-partial-reuse-partial": true,
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response body is \"01234\", not \"234\""
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response body is \"01234\", not \"234\""
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response body is \"01234\", not \"4\""
+  ],
+  "partial-use-headers": [
+    "Setup",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-use-stored-headers": [
+    "Setup",
+    "Response 2 status is 200, not 206"
+  ],
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": true,
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-sie-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-become": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-stored": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate-window": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-302-stale": true,
+  "status-303-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-303-stale": true,
+  "status-307-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-307-stale": true,
+  "status-308-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-308-stale": true,
+  "status-400-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-500-stale": true,
+  "status-502-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-502-stale": true,
+  "status-503-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-503-stale": true,
+  "status-504-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": true,
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
diff --git a/test/fixtures/cache-tests/results/chrome.json b/test/fixtures/cache-tests/results/chrome.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/chrome.json
@@ -0,0 +1,599 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": [
+    "Assertion",
+    "Response 2 header Clear-Site-Data is \"null\", not \"cookies\""
+  ],
+  "304-etag-update-response-Content-Encoding": [
+    "Assertion",
+    "Response 2 header Content-Encoding is \"arizqhypgxofwne\", not \"askcumewogyqias\""
+  ],
+  "304-etag-update-response-Content-Foo": true,
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": [
+    "Assertion",
+    "Response 2 header Content-Location is \"/foo\", not \"/bar\""
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Assertion",
+    "Response 2 header Content-MD5 is \"rL0Y20zC+Fzt72VPzMSk2A==\", not \"N7UdGUp1E+RbVvZSTy1R8g==\""
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Assertion",
+    "Response 2 header Content-Range is \"apetixmbqfujync\", not \"aqgwmcsiyoeukaq\""
+  ],
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": [
+    "Assertion",
+    "Response 2 header Content-Type is \"text/plain\", not \"text/plain;charset=utf-8\""
+  ],
+  "304-etag-update-response-ETag": [
+    "Assertion",
+    "Response 2 header ETag is \"\"abcdef\"\", not \"\"ghijkl\"\""
+  ],
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": [
+    "Assertion",
+    "Response 2 header X-Content-Foo is \"azyxwvutsrqponm\", not \"aaaaaaaaaaaaaaa\""
+  ],
+  "304-etag-update-response-X-Frame-Options": [
+    "Assertion",
+    "Response 2 header X-Frame-Options is \"deny\", not \"sameorigin\""
+  ],
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": [
+    "Assertion",
+    "Response 2 header X-XSS-Protection is \"1\", not \"1; mode=block\""
+  ],
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": true,
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": true,
+  "age-parse-suffix-twoline": true,
+  "cc-resp-immutable-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cc-resp-immutable-stale": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": true,
+  "cc-resp-no-cache-revalidate-fresh": true,
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-private": true,
+  "ccreq-ma0": true,
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-max-stale-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-min-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-min-fresh-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache": true,
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": [
+    "Assertion",
+    "Response 1 status is 200, not 504"
+  ],
+  "conditional-etag-forward": true,
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"\"abcdef\"\""
+  ],
+  "conditional-etag-weak-generate-weak": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": true,
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-2-digit-year": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-aest": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-spaces": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-no-comma": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-time-periods": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-utc": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": true,
+  "freshness-expires-wrong-case-tz": true,
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-decimal-zero": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-private": true,
+  "freshness-max-age-s-maxage-private-multiple": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": true,
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": true,
+  "freshness-max-age-two-fresh-stale-sepline": true,
+  "freshness-max-age-two-stale-fresh-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-none": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-writethrough": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": true,
+  "headers-omit-headers-listed-in-Connection": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": [
+    "Assertion",
+    "Response 2 header Clear-Site-Data is \"null\", not \"cookies\""
+  ],
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "headers-store-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"null\", not \"a=c\""
+  ],
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": true,
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": true,
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": true,
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": true,
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": true,
+  "heuristic-delta-1800": true,
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": true,
+  "heuristic-delta-3600": true,
+  "heuristic-delta-43200": true,
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": true,
+  "heuristic-delta-600": true,
+  "heuristic-delta-86400": true,
+  "invalidate-DELETE": true,
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": true,
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": true,
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "other-age-update-expires": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-age-update-max-age": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-cookie": true,
+  "other-date-update": true,
+  "other-date-update-expires": true,
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": true,
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": true,
+  "partial-store-complete-reuse-partial-no-last": true,
+  "partial-store-complete-reuse-partial-suffix": true,
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": true,
+  "partial-use-stored-headers": true,
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-close-must-revalidate": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-close-no-cache": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-sie-503": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-sie-close": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-warning-become": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-warning-stored": [
+    "TypeError",
+    "Failed to fetch"
+  ],
+  "stale-while-revalidate": true,
+  "stale-while-revalidate-window": true,
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": true,
+  "status-299-stale": true,
+  "status-400-fresh": true,
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": true,
+  "status-499-stale": true,
+  "status-500-fresh": true,
+  "status-500-stale": true,
+  "status-502-fresh": true,
+  "status-502-stale": true,
+  "status-503-fresh": true,
+  "status-503-stale": true,
+  "status-504-fresh": true,
+  "status-504-stale": true,
+  "status-599-fresh": true,
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": true,
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": true,
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/results/fastly.json b/test/fixtures/cache-tests/results/fastly.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/fastly.json
@@ -0,0 +1,882 @@
+{
+  "304-etag-update-response-Cache-Control": [
+    "Assertion",
+    "Response 2 header Cache-Control is \"max-age=1\", not \"max-age=3600\""
+  ],
+  "304-etag-update-response-Clear-Site-Data": [
+    "Assertion",
+    "Response 2 header Clear-Site-Data is \"cache\", not \"cookies\""
+  ],
+  "304-etag-update-response-Content-Encoding": [
+    "Assertion",
+    "Response 2 header Content-Encoding is \"arizqhypgxofwne\", not \"askcumewogyqias\""
+  ],
+  "304-etag-update-response-Content-Foo": [
+    "Assertion",
+    "Response 2 header Content-Foo is \"awsokgcyuqmieaw\", not \"axurolifczwtqnk\""
+  ],
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": [
+    "Assertion",
+    "Response 2 header Content-Location is \"/foo\", not \"/bar\""
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Assertion",
+    "Response 2 header Content-MD5 is \"rL0Y20zC+Fzt72VPzMSk2A==\", not \"N7UdGUp1E+RbVvZSTy1R8g==\""
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Assertion",
+    "Response 2 header Content-Range is \"null\", not \"aqgwmcsiyoeukaq\""
+  ],
+  "304-etag-update-response-Content-Security-Policy": [
+    "Assertion",
+    "Response 2 header Content-Security-Policy is \"default-src 'self'\", not \"default-src 'self' cdn.example.com\""
+  ],
+  "304-etag-update-response-Content-Type": [
+    "Assertion",
+    "Response 2 header Content-Type is \"text/plain\", not \"text/plain;charset=utf-8\""
+  ],
+  "304-etag-update-response-ETag": [
+    "Assertion",
+    "Response 2 header ETag is \"\"abcdef\"\", not \"\"ghijkl\"\""
+  ],
+  "304-etag-update-response-Expires": [
+    "Assertion",
+    "Response 2 header Expires is \"Fri, 01 Jan 2038 01:01:01 GMT\", not \"Mon, 11 Jan 2038 11:11:11 GMT\""
+  ],
+  "304-etag-update-response-Public-Key-Pins": [
+    "Assertion",
+    "Response 2 header Public-Key-Pins is \"auoicwqkeysmgau\", not \"avqlgbwrmhcxsni\""
+  ],
+  "304-etag-update-response-Set-Cookie": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"a=b\", not \"a=c\""
+  ],
+  "304-etag-update-response-Test-Header": [
+    "Assertion",
+    "Response 2 header Test-Header is \"aaaaaaaaaaaaaaa\", not \"abcdefghijklmno\""
+  ],
+  "304-etag-update-response-X-Content-Foo": [
+    "Assertion",
+    "Response 2 header X-Content-Foo is \"azyxwvutsrqponm\", not \"aaaaaaaaaaaaaaa\""
+  ],
+  "304-etag-update-response-X-Frame-Options": [
+    "Assertion",
+    "Response 2 header X-Frame-Options is \"deny\", not \"sameorigin\""
+  ],
+  "304-etag-update-response-X-Test-Header": [
+    "Assertion",
+    "Response 2 header X-Test-Header is \"adgjmpsvybehknq\", not \"aeimquycgkoswae\""
+  ],
+  "304-etag-update-response-X-XSS-Protection": [
+    "Assertion",
+    "Response 2 header X-XSS-Protection is \"1\", not \"1; mode=block\""
+  ],
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": true,
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": true,
+  "age-parse-parameter": true,
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": true,
+  "age-parse-suffix-twoline": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cc-resp-no-cache-case-insensitive": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cc-resp-no-cache-revalidate": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-cache-revalidate-fresh": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-max-stale-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-min-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-min-fresh-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": [
+    "Assertion",
+    "Response 1 status is 200, not 504"
+  ],
+  "cdn-cc-invalid-sh-type-unknown": true,
+  "cdn-cc-invalid-sh-type-wrong": true,
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 01:17:30 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-age": true,
+  "cdn-max-age-case-insensitive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-cc-max-age-invalid-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-extension": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-long-cc-max-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-private": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-remove-age-exceed": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "cdn-remove-header": true,
+  "conditional-304-etag": true,
+  "conditional-etag-forward": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": true,
+  "conditional-etag-quoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": true,
+  "conditional-etag-strong-respond-multiple-last": true,
+  "conditional-etag-strong-respond-multiple-second": true,
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-quoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-unquoted": true,
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": true,
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": true,
+  "conditional-etag-weak-respond-lowercase": true,
+  "conditional-etag-weak-respond-omit-slash": true,
+  "conditional-lm-fresh": true,
+  "conditional-lm-fresh-earlier": true,
+  "conditional-lm-fresh-no-lm": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh-rfc850": true,
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-age-slow-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-2-digit-year": true,
+  "freshness-expires-invalid-aest": true,
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": true,
+  "freshness-expires-invalid-multiple-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-spaces": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-no-comma": true,
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": true,
+  "freshness-expires-old-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": true,
+  "freshness-expires-wrong-case-tz": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": true,
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-decimal-five": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-decimal-zero": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": true,
+  "freshness-max-age-two-fresh-stale-sepline": true,
+  "freshness-max-age-two-stale-fresh-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 status is 410, not 200"
+  ],
+  "head-writethrough": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-omit-headers-listed-in-Connection": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": [
+    "Assertion",
+    "Response 2 header Content-Range is \"null\", not \"ananananananana\""
+  ],
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": [
+    "Setup",
+    "Response 1 status is 503, not 200"
+  ],
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": [
+    "Setup",
+    "retry"
+  ],
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1800": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-3600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-43200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-86400": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "invalidate-DELETE": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 header age is 0, should be bigger than 0"
+  ],
+  "other-age-gen": true,
+  "other-age-update-expires": [
+    "Assertion",
+    "Response 2 header Age is 3, should be bigger than 32"
+  ],
+  "other-age-update-max-age": true,
+  "other-authorization": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "other-authorization-must-revalidate": true,
+  "other-authorization-public": true,
+  "other-authorization-smaxage": true,
+  "other-cookie": true,
+  "other-date-update": [
+    "Assertion",
+    "Response 2 header Date is \"Tue, 09 Jul 2024 01:17:25 GMT\", not \"Tue, 09 Jul 2024 01:17:22 GMT\""
+  ],
+  "other-date-update-expires": [
+    "Assertion",
+    "Response 2 header Date is \"Tue, 09 Jul 2024 01:17:25 GMT\", not \"Tue, 09 Jul 2024 01:17:22 GMT\""
+  ],
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-set-cookie": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial": true,
+  "partial-store-complete-reuse-partial-no-last": true,
+  "partial-store-complete-reuse-partial-suffix": true,
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": true,
+  "partial-use-stored-headers": true,
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": true,
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": true,
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Setup",
+    "retry"
+  ],
+  "stale-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-sie-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-become": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-stored": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate": true,
+  "stale-while-revalidate-window": true,
+  "status-200-fresh": true,
+  "status-200-must-understand": true,
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": true,
+  "status-302-stale": true,
+  "status-303-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-303-stale": true,
+  "status-307-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-307-stale": true,
+  "status-308-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-308-stale": true,
+  "status-400-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": [
+    "Setup",
+    "retry"
+  ],
+  "status-500-stale": [
+    "Setup",
+    "retry"
+  ],
+  "status-502-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-502-stale": true,
+  "status-503-fresh": [
+    "Setup",
+    "retry"
+  ],
+  "status-503-stale": [
+    "Setup",
+    "retry"
+  ],
+  "status-504-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-empty-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-empty-star-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-foo-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-foo": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-star-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ]
+}
diff --git a/test/fixtures/cache-tests/results/firefox.json b/test/fixtures/cache-tests/results/firefox.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/firefox.json
@@ -0,0 +1,587 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": true,
+  "304-etag-update-response-Content-Encoding": [
+    "Assertion",
+    "Response 2 header Content-Encoding is \"arizqhypgxofwne\", not \"askcumewogyqias\""
+  ],
+  "304-etag-update-response-Content-Foo": true,
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": [
+    "Assertion",
+    "Response 2 header Content-Location is \"/foo\", not \"/bar\""
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Assertion",
+    "Response 2 header Content-MD5 is \"rL0Y20zC+Fzt72VPzMSk2A==\", not \"N7UdGUp1E+RbVvZSTy1R8g==\""
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Assertion",
+    "Response 2 header Content-Range is \"apetixmbqfujync\", not \"aqgwmcsiyoeukaq\""
+  ],
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": [
+    "Assertion",
+    "Response 2 header Content-Type is \"text/plain\", not \"text/plain;charset=utf-8\""
+  ],
+  "304-etag-update-response-ETag": [
+    "Assertion",
+    "Response 2 header ETag is \"\"abcdef\"\", not \"\"ghijkl\"\""
+  ],
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": true,
+  "304-etag-update-response-X-Frame-Options": true,
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": true,
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": true,
+  "age-parse-parameter": true,
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": true,
+  "age-parse-suffix-twoline": true,
+  "cc-resp-immutable-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cc-resp-immutable-stale": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cc-resp-no-cache-revalidate": true,
+  "cc-resp-no-cache-revalidate-fresh": true,
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-private": true,
+  "ccreq-ma0": true,
+  "ccreq-ma1": true,
+  "ccreq-magreaterage": true,
+  "ccreq-max-stale": true,
+  "ccreq-max-stale-age": true,
+  "ccreq-min-fresh": true,
+  "ccreq-min-fresh-age": true,
+  "ccreq-no-cache": true,
+  "ccreq-no-cache-etag": true,
+  "ccreq-no-cache-lm": true,
+  "ccreq-no-store": true,
+  "ccreq-oic": [
+    "Assertion",
+    "Response 1 status is 200, not 504"
+  ],
+  "conditional-etag-forward": true,
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"\"abcdef\"\""
+  ],
+  "conditional-etag-weak-generate-weak": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": true,
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-2-digit-year": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-aest": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-spaces": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-no-comma": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-time-periods": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-utc": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": true,
+  "freshness-expires-wrong-case-tz": true,
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": true,
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": true,
+  "freshness-max-age-decimal-zero": true,
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-private": true,
+  "freshness-max-age-s-maxage-private-multiple": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-fresh-stale-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sameline": true,
+  "freshness-max-age-two-stale-fresh-sepline": true,
+  "freshness-none": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-writethrough": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Connection": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "headers-store-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"null\", not \"a=c\""
+  ],
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": true,
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": true,
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": true,
+  "heuristic-204-cached": true,
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": true,
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": true,
+  "heuristic-delta-1800": true,
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": true,
+  "heuristic-delta-3600": true,
+  "heuristic-delta-43200": true,
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": true,
+  "heuristic-delta-600": true,
+  "heuristic-delta-86400": true,
+  "invalidate-DELETE": true,
+  "invalidate-DELETE-cl": true,
+  "invalidate-DELETE-failed": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "invalidate-DELETE-location": true,
+  "invalidate-M-SEARCH": true,
+  "invalidate-M-SEARCH-cl": true,
+  "invalidate-M-SEARCH-failed": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "invalidate-M-SEARCH-location": true,
+  "invalidate-POST": true,
+  "invalidate-POST-cl": true,
+  "invalidate-POST-failed": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "invalidate-POST-location": true,
+  "invalidate-PUT": true,
+  "invalidate-PUT-cl": true,
+  "invalidate-PUT-failed": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "invalidate-PUT-location": true,
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "other-age-update-expires": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-age-update-max-age": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-cookie": true,
+  "other-date-update": true,
+  "other-date-update-expires": true,
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": true,
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial-no-last": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-stored-headers": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": true,
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": true,
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-close-must-revalidate": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-close-no-cache": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-sie-503": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-sie-close": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-warning-become": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-warning-stored": [
+    "TypeError",
+    "NetworkError when attempting to fetch resource."
+  ],
+  "stale-while-revalidate": true,
+  "stale-while-revalidate-window": true,
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-299-stale": true,
+  "status-400-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-400-stale": true,
+  "status-404-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-500-stale": true,
+  "status-502-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-502-stale": true,
+  "status-503-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-503-stale": true,
+  "status-504-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": true,
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": true,
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/results/index.mjs b/test/fixtures/cache-tests/results/index.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/index.mjs
@@ -0,0 +1,71 @@
+
+export default [
+  {
+    file: 'chrome.json',
+    name: 'Chrome',
+    type: 'browser',
+    version: '126.0.6478.127'
+  },
+  {
+    file: 'firefox.json',
+    name: 'Firefox',
+    type: 'browser',
+    version: '127.0.2',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Firefox'
+  },
+  {
+    file: 'safari.json',
+    name: 'Safari',
+    type: 'browser',
+    version: 'Version 17.5 (19618.2.12.11.6)'
+  },
+  {
+    file: 'nginx.json',
+    name: 'nginx',
+    type: 'rev-proxy',
+    version: '1.26.0-1ubuntu2',
+    link: 'https://github.com/http-tests/cache-tests/wiki/nginx'
+  },
+  {
+    file: 'squid.json',
+    name: 'Squid',
+    type: 'rev-proxy',
+    version: '6.9-1ubuntu1',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Squid'
+  },
+  {
+    file: 'trafficserver.json',
+    name: 'ATS',
+    type: 'rev-proxy',
+    version: '9.2.4+ds-2',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Traffic-Server'
+  },
+  {
+    file: 'apache.json',
+    name: 'httpd',
+    type: 'rev-proxy',
+    version: '2.4.59-2ubuntu2',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Apache-httpd'
+  },
+  {
+    file: 'varnish.json',
+    name: 'Varnish',
+    type: 'rev-proxy',
+    version: '7.1.1-1.1ubuntu1',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Varnish'
+  },
+  {
+    file: 'caddy.json',
+    name: 'caddy',
+    type: 'rev-proxy',
+    version: '0.7.0',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Caddy'
+  },
+  {
+    file: 'fastly.json',
+    name: 'Fastly',
+    type: 'cdn',
+    version: '2024-07-09',
+    link: 'https://github.com/http-tests/cache-tests/wiki/Fastly'
+  }
+]
diff --git a/test/fixtures/cache-tests/results/nginx.json b/test/fixtures/cache-tests/results/nginx.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/nginx.json
@@ -0,0 +1,849 @@
+{
+  "304-etag-update-response-Cache-Control": [
+    "Assertion",
+    "Response 2 header Cache-Control is \"max-age=1\", not \"max-age=3600\""
+  ],
+  "304-etag-update-response-Clear-Site-Data": [
+    "Assertion",
+    "Response 2 header Clear-Site-Data is \"cache\", not \"cookies\""
+  ],
+  "304-etag-update-response-Content-Encoding": [
+    "Assertion",
+    "Response 2 header Content-Encoding is \"arizqhypgxofwne\", not \"askcumewogyqias\""
+  ],
+  "304-etag-update-response-Content-Foo": [
+    "AbortError",
+    "The user aborted a request."
+  ],
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": [
+    "Assertion",
+    "Response 2 header Content-Location is \"/foo\", not \"/bar\""
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Assertion",
+    "Response 2 header Content-MD5 is \"rL0Y20zC+Fzt72VPzMSk2A==\", not \"N7UdGUp1E+RbVvZSTy1R8g==\""
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Assertion",
+    "Response 2 header Content-Range is \"apetixmbqfujync\", not \"aqgwmcsiyoeukaq\""
+  ],
+  "304-etag-update-response-Content-Security-Policy": [
+    "Assertion",
+    "Response 2 header Content-Security-Policy is \"default-src 'self'\", not \"default-src 'self' cdn.example.com\""
+  ],
+  "304-etag-update-response-Content-Type": [
+    "AbortError",
+    "The user aborted a request."
+  ],
+  "304-etag-update-response-ETag": [
+    "AbortError",
+    "The user aborted a request."
+  ],
+  "304-etag-update-response-Expires": [
+    "Assertion",
+    "Response 2 header Expires is \"Fri, 01 Jan 2038 01:01:01 GMT\", not \"Mon, 11 Jan 2038 11:11:11 GMT\""
+  ],
+  "304-etag-update-response-Public-Key-Pins": [
+    "Assertion",
+    "Response 2 header Public-Key-Pins is \"auoicwqkeysmgau\", not \"avqlgbwrmhcxsni\""
+  ],
+  "304-etag-update-response-Set-Cookie": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"a=b\", not \"a=c\""
+  ],
+  "304-etag-update-response-Test-Header": [
+    "Assertion",
+    "Response 2 header Test-Header is \"aaaaaaaaaaaaaaa\", not \"abcdefghijklmno\""
+  ],
+  "304-etag-update-response-X-Content-Foo": [
+    "Assertion",
+    "Response 2 header X-Content-Foo is \"azyxwvutsrqponm\", not \"aaaaaaaaaaaaaaa\""
+  ],
+  "304-etag-update-response-X-Frame-Options": [
+    "Assertion",
+    "Response 2 header X-Frame-Options is \"deny\", not \"sameorigin\""
+  ],
+  "304-etag-update-response-X-Test-Header": [
+    "Assertion",
+    "Response 2 header X-Test-Header is \"adgjmpsvybehknq\", not \"aeimquycgkoswae\""
+  ],
+  "304-etag-update-response-X-XSS-Protection": [
+    "Assertion",
+    "Response 2 header X-XSS-Protection is \"1\", not \"1; mode=block\""
+  ],
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": true,
+  "age-parse-large": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-large-minus-one": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-larger": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-suffix-twoline": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-cache-revalidate-fresh": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-max-stale-age": true,
+  "ccreq-min-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-min-fresh-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": [
+    "Assertion",
+    "Response 1 status is 200, not 504"
+  ],
+  "cdn-cc-invalid-sh-type-unknown": true,
+  "cdn-cc-invalid-sh-type-wrong": true,
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 01:06:50 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-age": true,
+  "cdn-max-age-case-insensitive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-cc-max-age-invalid-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-extension": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-long-cc-max-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-private": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-remove-age-exceed": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "cdn-remove-header": true,
+  "conditional-304-etag": true,
+  "conditional-etag-forward": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-quoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": true,
+  "conditional-etag-strong-respond-multiple-last": true,
+  "conditional-etag-strong-respond-multiple-second": true,
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-quoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-unquoted": true,
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": true,
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": true,
+  "conditional-etag-weak-respond-lowercase": true,
+  "conditional-etag-weak-respond-omit-slash": true,
+  "conditional-lm-fresh": true,
+  "conditional-lm-fresh-earlier": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh-no-lm": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh-rfc850": true,
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-age-slow-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": true,
+  "freshness-expires-invalid-2-digit-year": true,
+  "freshness-expires-invalid-aest": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": true,
+  "freshness-expires-invalid-multiple-lines": true,
+  "freshness-expires-invalid-multiple-spaces": true,
+  "freshness-expires-invalid-no-comma": true,
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-old-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-past": true,
+  "freshness-expires-present": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": true,
+  "freshness-expires-wrong-case-tz": true,
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-decimal-five": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-decimal-zero": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": true,
+  "freshness-max-age-space-before-equals": true,
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": true,
+  "freshness-max-age-two-fresh-stale-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sepline": true,
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 status is 410, not 200"
+  ],
+  "head-writethrough": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Connection": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": [
+    "Setup",
+    "Response 1 status is 502, not 200"
+  ],
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1800": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-3600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-43200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-86400": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "invalidate-DELETE": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": [
+    "AbortError",
+    "The user aborted a request."
+  ],
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "other-age-update-expires": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-age-update-max-age": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-authorization": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "other-authorization-must-revalidate": true,
+  "other-authorization-public": true,
+  "other-authorization-smaxage": true,
+  "other-cookie": true,
+  "other-date-update": [
+    "Assertion",
+    "Response 2 header Date is \"Tue, 09 Jul 2024 01:06:44 GMT\", not \"Tue, 09 Jul 2024 01:06:41 GMT\""
+  ],
+  "other-date-update-expires": [
+    "Assertion",
+    "Response 2 header Date is \"Tue, 09 Jul 2024 01:06:44 GMT\", not \"Tue, 09 Jul 2024 01:06:41 GMT\""
+  ],
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-set-cookie": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial": [
+    "Assertion",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-store-complete-reuse-partial-no-last": [
+    "Assertion",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-store-complete-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-store-partial-complete": [
+    "Setup",
+    "Response 2 status is 206, not 200"
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Setup",
+    "Request 1 header Range is \"undefined\", not \"bytes=-5\""
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response body is \"01234\", not \"234\""
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response body is \"01234\", not \"234\""
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response body is \"01234\", not \"4\""
+  ],
+  "partial-use-headers": [
+    "Setup",
+    "Response 2 status is 200, not 206"
+  ],
+  "partial-use-stored-headers": [
+    "Setup",
+    "Response 2 status is 200, not 206"
+  ],
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": true,
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": true,
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": true,
+  "stale-sie-close": true,
+  "stale-warning-become": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-stored": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate-window": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": true,
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": true,
+  "status-302-stale": true,
+  "status-303-fresh": true,
+  "status-303-stale": true,
+  "status-307-fresh": true,
+  "status-307-stale": true,
+  "status-308-fresh": true,
+  "status-308-stale": true,
+  "status-400-fresh": true,
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": true,
+  "status-499-stale": true,
+  "status-500-fresh": true,
+  "status-500-stale": true,
+  "status-502-fresh": true,
+  "status-502-stale": true,
+  "status-503-fresh": true,
+  "status-503-stale": true,
+  "status-504-fresh": true,
+  "status-504-stale": true,
+  "status-599-fresh": true,
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": true,
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-empty-star-lines": true,
+  "vary-syntax-foo-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-star-lines": true
+}
diff --git a/test/fixtures/cache-tests/results/safari.json b/test/fixtures/cache-tests/results/safari.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/safari.json
@@ -0,0 +1,611 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": true,
+  "304-etag-update-response-Content-Encoding": [
+    "Assertion",
+    "Response 2 header Content-Encoding is \"arizqhypgxofwne\", not \"askcumewogyqias\""
+  ],
+  "304-etag-update-response-Content-Foo": [
+    "Assertion",
+    "Response 2 header Content-Foo is \"awsokgcyuqmieaw\", not \"axurolifczwtqnk\""
+  ],
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": [
+    "Assertion",
+    "Response 2 header Content-Location is \"/foo\", not \"/bar\""
+  ],
+  "304-etag-update-response-Content-MD5": [
+    "Assertion",
+    "Response 2 header Content-MD5 is \"rL0Y20zC+Fzt72VPzMSk2A==\", not \"N7UdGUp1E+RbVvZSTy1R8g==\""
+  ],
+  "304-etag-update-response-Content-Range": [
+    "Assertion",
+    "Response 2 header Content-Range is \"apetixmbqfujync\", not \"aqgwmcsiyoeukaq\""
+  ],
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": [
+    "Assertion",
+    "Response 2 header Content-Type is \"text/plain\", not \"text/plain;charset=utf-8\""
+  ],
+  "304-etag-update-response-ETag": [
+    "Assertion",
+    "Response 2 header ETag is \"\"abcdef\"\", not \"\"ghijkl\"\""
+  ],
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": [
+    "Assertion",
+    "Response 2 header X-Content-Foo is \"azyxwvutsrqponm\", not \"aaaaaaaaaaaaaaa\""
+  ],
+  "304-etag-update-response-X-Frame-Options": [
+    "Assertion",
+    "Response 2 header X-Frame-Options is \"deny\", not \"sameorigin\""
+  ],
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": [
+    "Assertion",
+    "Response 2 header X-XSS-Protection is \"1\", not \"1; mode=block\""
+  ],
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-suffix-twoline": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cc-resp-immutable-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cc-resp-immutable-stale": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": true,
+  "cc-resp-no-cache-revalidate-fresh": true,
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-private": true,
+  "ccreq-ma0": true,
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": true,
+  "ccreq-max-stale-age": true,
+  "ccreq-min-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-min-fresh-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache": true,
+  "ccreq-no-cache-etag": true,
+  "ccreq-no-cache-lm": true,
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": [
+    "TypeError",
+    "Load failed"
+  ],
+  "conditional-etag-forward": true,
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": true,
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-2-digit-year": true,
+  "freshness-expires-invalid-aest": true,
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-lines": true,
+  "freshness-expires-invalid-multiple-spaces": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-no-comma": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-month": true,
+  "freshness-expires-wrong-case-tz": true,
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": true,
+  "freshness-max-age-decimal-zero": true,
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": true,
+  "freshness-max-age-s-maxage-private": true,
+  "freshness-max-age-s-maxage-private-multiple": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": true,
+  "freshness-max-age-two-fresh-stale-sepline": true,
+  "freshness-max-age-two-stale-fresh-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-none": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-writethrough": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-omit-headers-listed-in-Connection": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "headers-store-Set-Cookie2": [
+    "Assertion",
+    "Response 2 header Set-Cookie2 is \"null\", not \"a=c\""
+  ],
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": true,
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": true,
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": true,
+  "heuristic-204-cached": true,
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": true,
+  "heuristic-405-cached": true,
+  "heuristic-410-cached": true,
+  "heuristic-414-cached": true,
+  "heuristic-501-cached": true,
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": true,
+  "heuristic-delta-1800": true,
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": true,
+  "heuristic-delta-3600": true,
+  "heuristic-delta-43200": true,
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": true,
+  "heuristic-delta-600": true,
+  "heuristic-delta-86400": true,
+  "invalidate-DELETE": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "other-age-update-expires": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-age-update-max-age": [
+    "Assertion",
+    "Response 2 header Age is 30, should be bigger than 32"
+  ],
+  "other-cookie": true,
+  "other-date-update": true,
+  "other-date-update-expires": true,
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": true,
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial-no-last": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": true,
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-stored-headers": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-close-must-revalidate": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-close-no-cache": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-sie-503": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-sie-close": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-warning-become": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-warning-stored": [
+    "TypeError",
+    "Load failed"
+  ],
+  "stale-while-revalidate": true,
+  "stale-while-revalidate-window": true,
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-299-stale": true,
+  "status-400-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-500-stale": true,
+  "status-502-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-502-stale": true,
+  "status-503-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-503-stale": true,
+  "status-504-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": true,
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": true,
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/results/squid.json b/test/fixtures/cache-tests/results/squid.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/squid.json
@@ -0,0 +1,681 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": true,
+  "304-etag-update-response-Content-Encoding": true,
+  "304-etag-update-response-Content-Foo": true,
+  "304-etag-update-response-Content-Length": [
+    "Assertion",
+    "Response 2 header Content-Length is \"10\", not \"36\""
+  ],
+  "304-etag-update-response-Content-Location": true,
+  "304-etag-update-response-Content-MD5": true,
+  "304-etag-update-response-Content-Range": true,
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": true,
+  "304-etag-update-response-ETag": true,
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Set-Cookie2": true,
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": true,
+  "304-etag-update-response-X-Frame-Options": true,
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": true,
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "age-parse-large": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-large-minus-one": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-larger": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": true,
+  "age-parse-parameter": true,
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": true,
+  "age-parse-suffix-twoline": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": true,
+  "cc-resp-no-cache-revalidate-fresh": true,
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": true,
+  "ccreq-ma1": true,
+  "ccreq-magreaterage": true,
+  "ccreq-max-stale": true,
+  "ccreq-max-stale-age": true,
+  "ccreq-min-fresh": true,
+  "ccreq-min-fresh-age": true,
+  "ccreq-no-cache": true,
+  "ccreq-no-cache-etag": [
+    "Setup",
+    "Response 2 status is 502, not 200"
+  ],
+  "ccreq-no-cache-lm": [
+    "Setup",
+    "Response 2 status is 502, not 200"
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": true,
+  "cdn-cc-invalid-sh-type-unknown": true,
+  "cdn-cc-invalid-sh-type-wrong": true,
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 00:51:04 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-age": true,
+  "cdn-max-age-case-insensitive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-cc-max-age-invalid-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-extension": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-long-cc-max-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-private": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-remove-age-exceed": [
+    "Assertion",
+    "Response 2 Age header not present."
+  ],
+  "cdn-remove-header": true,
+  "conditional-304-etag": [
+    "Assertion",
+    "Response 2 header ETag is \"null\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-forward": true,
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": true,
+  "conditional-etag-quoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "request 2 doesn't have if-none-match header"
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": true,
+  "conditional-etag-strong-respond-multiple-last": true,
+  "conditional-etag-strong-respond-multiple-second": true,
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-quoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": [
+    "Assertion",
+    "request 2 doesn't have if-none-match header"
+  ],
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-weak-respond-lowercase": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-weak-respond-omit-slash": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh": true,
+  "conditional-lm-fresh-earlier": true,
+  "conditional-lm-fresh-no-lm": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh-rfc850": true,
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": true,
+  "freshness-expires-invalid-2-digit-year": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-aest": true,
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-lines": true,
+  "freshness-expires-invalid-multiple-spaces": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-no-comma": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": true,
+  "freshness-expires-old-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": true,
+  "freshness-expires-wrong-case-tz": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": true,
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": true,
+  "freshness-max-age-decimal-zero": true,
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": true,
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": true,
+  "freshness-max-age-two-fresh-stale-sepline": true,
+  "freshness-max-age-two-stale-fresh-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-writethrough": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Connection": true,
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": [
+    "Setup",
+    "Response 1 status is 502, not 200"
+  ],
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": true,
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": true,
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": true,
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": true,
+  "heuristic-delta-1800": true,
+  "heuristic-delta-30": true,
+  "heuristic-delta-300": true,
+  "heuristic-delta-3600": true,
+  "heuristic-delta-43200": true,
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": true,
+  "heuristic-delta-600": true,
+  "heuristic-delta-86400": true,
+  "invalidate-DELETE": true,
+  "invalidate-DELETE-cl": true,
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": true,
+  "invalidate-M-SEARCH": true,
+  "invalidate-M-SEARCH-cl": true,
+  "invalidate-M-SEARCH-failed": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "invalidate-M-SEARCH-location": true,
+  "invalidate-POST": true,
+  "invalidate-POST-cl": true,
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": true,
+  "invalidate-PUT": true,
+  "invalidate-PUT-cl": true,
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": true,
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 age header not present."
+  ],
+  "other-age-gen": true,
+  "other-age-update-expires": true,
+  "other-age-update-max-age": true,
+  "other-authorization": [
+    "Setup",
+    "Request 1 header Authorization is \"undefined\", not \"FOO\""
+  ],
+  "other-authorization-must-revalidate": [
+    "Setup",
+    "Request 1 header Authorization is \"undefined\", not \"FOO\""
+  ],
+  "other-authorization-public": [
+    "Setup",
+    "Request 1 header Authorization is \"undefined\", not \"FOO\""
+  ],
+  "other-authorization-smaxage": [
+    "Setup",
+    "Request 1 header Authorization is \"undefined\", not \"FOO\""
+  ],
+  "other-cookie": true,
+  "other-date-update": true,
+  "other-date-update-expires": true,
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": true,
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": true,
+  "partial-store-complete-reuse-partial-no-last": true,
+  "partial-store-complete-reuse-partial-suffix": true,
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": true,
+  "partial-use-stored-headers": true,
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": true,
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": true,
+  "stale-close": true,
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": true,
+  "stale-sie-close": true,
+  "stale-warning-become": [
+    "Assertion",
+    "Response 2 warning header not present."
+  ],
+  "stale-warning-stored": [
+    "Assertion",
+    "Response 2 warning header not present."
+  ],
+  "stale-while-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate-window": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": true,
+  "status-302-stale": true,
+  "status-303-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-303-stale": true,
+  "status-307-fresh": true,
+  "status-307-stale": true,
+  "status-308-fresh": true,
+  "status-308-stale": true,
+  "status-400-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-400-stale": true,
+  "status-404-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-500-stale": true,
+  "status-502-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-502-stale": true,
+  "status-503-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-503-stale": true,
+  "status-504-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": true,
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": true,
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
diff --git a/test/fixtures/cache-tests/results/trafficserver.json b/test/fixtures/cache-tests/results/trafficserver.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/trafficserver.json
@@ -0,0 +1,678 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": true,
+  "304-etag-update-response-Content-Encoding": true,
+  "304-etag-update-response-Content-Foo": true,
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": true,
+  "304-etag-update-response-Content-MD5": true,
+  "304-etag-update-response-Content-Range": true,
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": [
+    "Assertion",
+    "Response 2 header Content-Type is \"text/plain\", not \"text/plain;charset=utf-8\""
+  ],
+  "304-etag-update-response-ETag": true,
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "304-etag-update-response-Set-Cookie2": true,
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": true,
+  "304-etag-update-response-X-Frame-Options": true,
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": true,
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": true,
+  "age-parse-parameter": true,
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": true,
+  "age-parse-suffix-twoline": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-cache-revalidate-fresh": true,
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": true,
+  "ccreq-max-stale-age": true,
+  "ccreq-min-fresh": true,
+  "ccreq-min-fresh-age": true,
+  "ccreq-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": true,
+  "cdn-cc-invalid-sh-type-unknown": true,
+  "cdn-cc-invalid-sh-type-wrong": true,
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 01:02:07 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-age": true,
+  "cdn-max-age-case-insensitive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-cc-max-age-invalid-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-extension": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-long-cc-max-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-private": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-remove-age-exceed": true,
+  "cdn-remove-header": true,
+  "conditional-304-etag": true,
+  "conditional-etag-forward": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": true,
+  "conditional-etag-quoted-respond-unquoted": true,
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": true,
+  "conditional-etag-strong-respond-multiple-last": true,
+  "conditional-etag-strong-respond-multiple-second": true,
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-quoted": true,
+  "conditional-etag-unquoted-respond-unquoted": true,
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-weak-respond-lowercase": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-weak-respond-omit-slash": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh": true,
+  "conditional-lm-fresh-earlier": true,
+  "conditional-lm-fresh-no-lm": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh-rfc850": true,
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": true,
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": true,
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-2-digit-year": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-aest": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-multiple-spaces": true,
+  "freshness-expires-invalid-no-comma": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-tz": true,
+  "freshness-expires-wrong-case-weekday": true,
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": true,
+  "freshness-max-age-a100": true,
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": true,
+  "freshness-max-age-decimal-five": true,
+  "freshness-max-age-decimal-zero": true,
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": true,
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-fresh-stale-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sameline": true,
+  "freshness-max-age-two-stale-fresh-sepline": true,
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Response 3 does not come from cache"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 does not come from cache"
+  ],
+  "head-writethrough": true,
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Connection": [
+    "Assertion",
+    "Response 2 includes unexpected header a: \"1\""
+  ],
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": true,
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Assertion",
+    "Response 2 header Set-Cookie is \"null\", not \"a=c\""
+  ],
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": [
+    "Setup",
+    "Response 1 status is 400, not 200"
+  ],
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": true,
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": true,
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": true,
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1800": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-3600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-43200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-86400": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "invalidate-DELETE": [
+    "Setup",
+    "Response 2 status is 403, not 200"
+  ],
+  "invalidate-DELETE-cl": [
+    "Setup",
+    "Response 2 status is 403, not 200"
+  ],
+  "invalidate-DELETE-failed": [
+    "Setup",
+    "Response 2 status is 403, not 500"
+  ],
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": true,
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": true,
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": true,
+  "other-age-gen": true,
+  "other-age-update-expires": true,
+  "other-age-update-max-age": true,
+  "other-authorization": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "other-authorization-must-revalidate": true,
+  "other-authorization-public": true,
+  "other-authorization-smaxage": true,
+  "other-cookie": true,
+  "other-date-update": true,
+  "other-date-update-expires": true,
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-set-cookie": true,
+  "partial-store-complete-reuse-partial": true,
+  "partial-store-complete-reuse-partial-no-last": true,
+  "partial-store-complete-reuse-partial-suffix": true,
+  "partial-store-partial-complete": [
+    "Assertion",
+    "Request 2 header range is \"undefined\", not \"bytes=5-\""
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-use-headers": true,
+  "partial-use-stored-headers": true,
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": true,
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": true,
+  "stale-close": true,
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": true,
+  "stale-sie-close": true,
+  "stale-warning-become": true,
+  "stale-warning-stored": true,
+  "stale-while-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate-window": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": true,
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": true,
+  "status-302-stale": true,
+  "status-303-fresh": true,
+  "status-303-stale": true,
+  "status-307-fresh": true,
+  "status-307-stale": true,
+  "status-308-fresh": true,
+  "status-308-stale": true,
+  "status-400-fresh": true,
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": true,
+  "status-499-stale": true,
+  "status-500-fresh": true,
+  "status-500-stale": true,
+  "status-502-fresh": true,
+  "status-502-stale": true,
+  "status-503-fresh": true,
+  "status-503-stale": true,
+  "status-504-fresh": true,
+  "status-504-stale": true,
+  "status-599-fresh": true,
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": true,
+  "vary-normalise-lang-case": true,
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": true,
+  "vary-normalise-space": true,
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": true,
+  "vary-syntax-empty-star-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-foo-star": true,
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": true,
+  "vary-syntax-star-star": true,
+  "vary-syntax-star-star-lines": true
+}
diff --git a/test/fixtures/cache-tests/results/varnish.json b/test/fixtures/cache-tests/results/varnish.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/results/varnish.json
@@ -0,0 +1,804 @@
+{
+  "304-etag-update-response-Cache-Control": true,
+  "304-etag-update-response-Clear-Site-Data": true,
+  "304-etag-update-response-Content-Encoding": [
+    "Assertion",
+    "Response 2 header Content-Encoding is \"arizqhypgxofwne\", not \"askcumewogyqias\""
+  ],
+  "304-etag-update-response-Content-Foo": true,
+  "304-etag-update-response-Content-Length": true,
+  "304-etag-update-response-Content-Location": true,
+  "304-etag-update-response-Content-MD5": true,
+  "304-etag-update-response-Content-Range": [
+    "Setup",
+    "Response 1 status is 503, not 200"
+  ],
+  "304-etag-update-response-Content-Security-Policy": true,
+  "304-etag-update-response-Content-Type": true,
+  "304-etag-update-response-ETag": true,
+  "304-etag-update-response-Expires": true,
+  "304-etag-update-response-Public-Key-Pins": true,
+  "304-etag-update-response-Set-Cookie": [
+    "Setup",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "304-etag-update-response-Set-Cookie2": true,
+  "304-etag-update-response-Test-Header": true,
+  "304-etag-update-response-X-Content-Foo": true,
+  "304-etag-update-response-X-Frame-Options": true,
+  "304-etag-update-response-X-Test-Header": true,
+  "304-etag-update-response-X-XSS-Protection": true,
+  "304-lm-use-stored-Test-Header": true,
+  "age-parse-dup-0": true,
+  "age-parse-dup-0-twoline": true,
+  "age-parse-dup-old": true,
+  "age-parse-float": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "age-parse-large": true,
+  "age-parse-large-minus-one": true,
+  "age-parse-larger": true,
+  "age-parse-negative": true,
+  "age-parse-nonnumeric": true,
+  "age-parse-numeric-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-parameter": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "age-parse-prefix": true,
+  "age-parse-prefix-twoline": true,
+  "age-parse-suffix": true,
+  "age-parse-suffix-twoline": true,
+  "cc-resp-must-revalidate-fresh": true,
+  "cc-resp-must-revalidate-stale": true,
+  "cc-resp-no-cache": true,
+  "cc-resp-no-cache-case-insensitive": true,
+  "cc-resp-no-cache-revalidate": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-cache-revalidate-fresh": [
+    "Assertion",
+    "Request 2 should have been conditional, but it was not."
+  ],
+  "cc-resp-no-store": true,
+  "cc-resp-no-store-case-insensitive": true,
+  "cc-resp-no-store-fresh": true,
+  "cc-resp-no-store-old-max-age": true,
+  "cc-resp-no-store-old-new": true,
+  "cc-resp-private-shared": true,
+  "ccreq-ma0": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-ma1": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-magreaterage": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-max-stale": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-max-stale-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "ccreq-min-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-min-fresh-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-no-cache-etag": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-cache-lm": [
+    "Assertion",
+    "request 2 wasn't sent to server"
+  ],
+  "ccreq-no-store": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "ccreq-oic": [
+    "Assertion",
+    "Response 1 status is 200, not 504"
+  ],
+  "cdn-cc-invalid-sh-type-unknown": true,
+  "cdn-cc-invalid-sh-type-wrong": true,
+  "cdn-date-update-exceed": true,
+  "cdn-expires-update-exceed": [
+    "Assertion",
+    "Response 2 header Expires is \"null\", not \"Tue, 09 Jul 2024 01:02:40 GMT\""
+  ],
+  "cdn-fresh-cc-nostore": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-0": true,
+  "cdn-max-age-0-expires": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-age": true,
+  "cdn-max-age-case-insensitive": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-cc-max-age-invalid-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-expires": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-extension": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-long-cc-max-age": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-max-age-max": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-max-plus": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-short-cc-max-age": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "cdn-max-age-space-after-equals": true,
+  "cdn-max-age-space-before-equals": true,
+  "cdn-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-no-store-cc-fresh": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-private": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "cdn-remove-age-exceed": true,
+  "cdn-remove-header": true,
+  "conditional-304-etag": true,
+  "conditional-etag-forward": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-forward-unquoted": [
+    "Assertion",
+    "Request 1 header If-None-Match is \"undefined\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-precedence": true,
+  "conditional-etag-quoted-respond-unquoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-generate": true,
+  "conditional-etag-strong-generate-unquoted": [
+    "Assertion",
+    "Request 2 header If-None-Match is \"abcdef\", not \"\"abcdef\"\""
+  ],
+  "conditional-etag-strong-respond": true,
+  "conditional-etag-strong-respond-multiple-first": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-respond-multiple-last": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-respond-multiple-second": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-strong-respond-obs-text": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-quoted": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-etag-unquoted-respond-unquoted": true,
+  "conditional-etag-vary-headers": true,
+  "conditional-etag-vary-headers-mismatch": true,
+  "conditional-etag-weak-generate-weak": true,
+  "conditional-etag-weak-respond": true,
+  "conditional-etag-weak-respond-backslash": true,
+  "conditional-etag-weak-respond-lowercase": true,
+  "conditional-etag-weak-respond-omit-slash": true,
+  "conditional-lm-fresh": true,
+  "conditional-lm-fresh-earlier": true,
+  "conditional-lm-fresh-no-lm": [
+    "Assertion",
+    "Response 2 status is 200, not 304"
+  ],
+  "conditional-lm-fresh-rfc850": true,
+  "conditional-lm-stale": true,
+  "freshness-expires-32bit": true,
+  "freshness-expires-age-fast-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-expires-age-slow-date": true,
+  "freshness-expires-ansi-c": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-far-future": true,
+  "freshness-expires-future": true,
+  "freshness-expires-invalid": true,
+  "freshness-expires-invalid-1-digit-hour": true,
+  "freshness-expires-invalid-2-digit-year": true,
+  "freshness-expires-invalid-aest": true,
+  "freshness-expires-invalid-date": true,
+  "freshness-expires-invalid-date-dashes": true,
+  "freshness-expires-invalid-multiple-lines": true,
+  "freshness-expires-invalid-multiple-spaces": true,
+  "freshness-expires-invalid-no-comma": true,
+  "freshness-expires-invalid-time-periods": true,
+  "freshness-expires-invalid-utc": true,
+  "freshness-expires-old-date": true,
+  "freshness-expires-past": true,
+  "freshness-expires-present": true,
+  "freshness-expires-rfc850": true,
+  "freshness-expires-wrong-case-month": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-tz": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-expires-wrong-case-weekday": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age": true,
+  "freshness-max-age-0": true,
+  "freshness-max-age-0-expires": true,
+  "freshness-max-age-100a": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-a100": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-age": true,
+  "freshness-max-age-case-insenstive": true,
+  "freshness-max-age-date": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-decimal-five": true,
+  "freshness-max-age-decimal-zero": true,
+  "freshness-max-age-expires": true,
+  "freshness-max-age-expires-invalid": true,
+  "freshness-max-age-extension": true,
+  "freshness-max-age-ignore-quoted": true,
+  "freshness-max-age-ignore-quoted-rev": true,
+  "freshness-max-age-leading-zero": true,
+  "freshness-max-age-max": true,
+  "freshness-max-age-max-minus-1": true,
+  "freshness-max-age-max-plus": true,
+  "freshness-max-age-max-plus-1": true,
+  "freshness-max-age-negative": true,
+  "freshness-max-age-quoted": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-s-maxage-shared-longer": true,
+  "freshness-max-age-s-maxage-shared-longer-multiple": true,
+  "freshness-max-age-s-maxage-shared-longer-reversed": true,
+  "freshness-max-age-s-maxage-shared-shorter": true,
+  "freshness-max-age-s-maxage-shared-shorter-expires": true,
+  "freshness-max-age-single-quoted": true,
+  "freshness-max-age-space-after-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-space-before-equals": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "freshness-max-age-stale": true,
+  "freshness-max-age-two-fresh-stale-sameline": true,
+  "freshness-max-age-two-fresh-stale-sepline": true,
+  "freshness-max-age-two-stale-fresh-sameline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-max-age-two-stale-fresh-sepline": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "freshness-none": true,
+  "freshness-s-maxage-shared": true,
+  "head-200-freshness-update": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "head-200-retain": [
+    "Assertion",
+    "Response 2 header Template-A is \"null\", not \"1\""
+  ],
+  "head-200-update": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "head-410-update": [
+    "Setup",
+    "Response 3 status is 410, not 200"
+  ],
+  "head-writethrough": [
+    "Assertion",
+    "Request 2 had method GET, not HEAD"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Cache-Control-no-cache-single": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-omit-headers-listed-in-Connection": true,
+  "headers-store-Cache-Control": true,
+  "headers-store-Clear-Site-Data": true,
+  "headers-store-Connection": true,
+  "headers-store-Content-Encoding": true,
+  "headers-store-Content-Foo": true,
+  "headers-store-Content-Length": true,
+  "headers-store-Content-Location": true,
+  "headers-store-Content-MD5": true,
+  "headers-store-Content-Range": [
+    "Setup",
+    "Response 1 status is 503, not 200"
+  ],
+  "headers-store-Content-Security-Policy": true,
+  "headers-store-Content-Type": true,
+  "headers-store-ETag": true,
+  "headers-store-Expires": true,
+  "headers-store-Keep-Alive": true,
+  "headers-store-Proxy-Authenticate": true,
+  "headers-store-Proxy-Authentication-Info": true,
+  "headers-store-Proxy-Authorization": true,
+  "headers-store-Proxy-Connection": true,
+  "headers-store-Public-Key-Pins": true,
+  "headers-store-Set-Cookie": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "headers-store-Set-Cookie2": true,
+  "headers-store-TE": true,
+  "headers-store-Test-Header": true,
+  "headers-store-Transfer-Encoding": [
+    "Setup",
+    "Response 1 status is 503, not 200"
+  ],
+  "headers-store-Upgrade": true,
+  "headers-store-X-Content-Foo": true,
+  "headers-store-X-Frame-Options": true,
+  "headers-store-X-Test-Header": true,
+  "headers-store-X-XSS-Protection": true,
+  "heuristic-200-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-201-not_cached": true,
+  "heuristic-202-not_cached": true,
+  "heuristic-203-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-204-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-403-not_cached": true,
+  "heuristic-404-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-405-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-410-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-414-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-501-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-502-not_cached": true,
+  "heuristic-503-not_cached": true,
+  "heuristic-504-not_cached": true,
+  "heuristic-599-cached": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-599-not_cached": true,
+  "heuristic-delta-10": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-1800": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-30": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-300": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-3600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-43200": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-5": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-60": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-600": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "heuristic-delta-86400": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "invalidate-DELETE": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-DELETE-failed": true,
+  "invalidate-DELETE-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-M-SEARCH-failed": true,
+  "invalidate-M-SEARCH-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-POST-failed": true,
+  "invalidate-POST-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-cl": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "invalidate-PUT-failed": true,
+  "invalidate-PUT-location": [
+    "Assertion",
+    "Response 3 comes from cache"
+  ],
+  "method-POST": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-age-delay": [
+    "Assertion",
+    "Response 1 header age is 0, should be bigger than 0"
+  ],
+  "other-age-gen": true,
+  "other-age-update-expires": true,
+  "other-age-update-max-age": true,
+  "other-authorization": true,
+  "other-authorization-must-revalidate": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-authorization-public": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-authorization-smaxage": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-cookie": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-date-update": true,
+  "other-date-update-expires": true,
+  "other-date-update-expires-update": true,
+  "other-fresh-content-disposition-attachment": true,
+  "other-heuristic-content-disposition-attachment": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "other-set-cookie": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "partial-store-complete-reuse-partial": true,
+  "partial-store-complete-reuse-partial-no-last": true,
+  "partial-store-complete-reuse-partial-suffix": true,
+  "partial-store-partial-complete": [
+    "Setup",
+    "Response 1 status is 503, not 206"
+  ],
+  "partial-store-partial-reuse-partial": [
+    "Setup",
+    "Response 1 status is 503, not 206"
+  ],
+  "partial-store-partial-reuse-partial-absent": [
+    "Setup",
+    "Response 1 status is 503, not 206"
+  ],
+  "partial-store-partial-reuse-partial-byterange": [
+    "Setup",
+    "Response 1 status is 503, not 206"
+  ],
+  "partial-store-partial-reuse-partial-suffix": [
+    "Setup",
+    "Response 1 status is 503, not 206"
+  ],
+  "partial-use-headers": true,
+  "partial-use-stored-headers": true,
+  "pragma-request-extension": true,
+  "pragma-request-no-cache": true,
+  "pragma-response-extension": true,
+  "pragma-response-no-cache": true,
+  "pragma-response-no-cache-heuristic": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "query-args-different": true,
+  "query-args-same": true,
+  "stale-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-close-must-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-no-cache": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-proxy-revalidate": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-close-s-maxage=2": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "stale-sie-503": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-sie-close": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-become": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-warning-stored": [
+    "Setup",
+    "Response 2 does not come from cache"
+  ],
+  "stale-while-revalidate": true,
+  "stale-while-revalidate-window": true,
+  "status-200-fresh": true,
+  "status-200-must-understand": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-200-stale": true,
+  "status-203-fresh": true,
+  "status-203-stale": true,
+  "status-204-fresh": true,
+  "status-204-stale": true,
+  "status-299-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-299-stale": true,
+  "status-301-fresh": true,
+  "status-301-stale": true,
+  "status-302-fresh": true,
+  "status-302-stale": true,
+  "status-303-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-303-stale": true,
+  "status-307-fresh": true,
+  "status-307-stale": true,
+  "status-308-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-308-stale": true,
+  "status-400-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-400-stale": true,
+  "status-404-fresh": true,
+  "status-404-stale": true,
+  "status-410-fresh": true,
+  "status-410-stale": true,
+  "status-499-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-499-stale": true,
+  "status-500-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-500-stale": true,
+  "status-502-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-502-stale": true,
+  "status-503-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-503-stale": true,
+  "status-504-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-504-stale": true,
+  "status-599-fresh": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "status-599-must-understand": true,
+  "status-599-stale": true,
+  "vary-2-match": true,
+  "vary-2-match-omit": true,
+  "vary-2-no-match": true,
+  "vary-3-match": true,
+  "vary-3-no-match": true,
+  "vary-3-omit": true,
+  "vary-3-order": true,
+  "vary-cache-key": true,
+  "vary-invalidate": true,
+  "vary-match": true,
+  "vary-no-match": true,
+  "vary-normalise-combine": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-case": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-order": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-select": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-lang-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-normalise-space": [
+    "Assertion",
+    "Response 2 does not come from cache"
+  ],
+  "vary-omit": true,
+  "vary-omit-stored": true,
+  "vary-star": true,
+  "vary-syntax-empty-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-empty-star-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-foo-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star": true,
+  "vary-syntax-star-foo": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-star": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ],
+  "vary-syntax-star-star-lines": [
+    "Assertion",
+    "Response 2 comes from cache"
+  ]
+}
diff --git a/test/fixtures/cache-tests/spec/Makefile b/test/fixtures/cache-tests/spec/Makefile
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/Makefile
@@ -0,0 +1,24 @@
+saxpath ?= "lib/saxon9.jar"
+saxon ?= java -classpath $(saxpath) net.sf.saxon.Transform -l
+themedir ?= ../../../httpwg-theme
+saxparam ?= bootstrapCssUrl='/spec/bootstrap.min.css' \
+						siteJsUrl='/spec/script.mjs' \
+						siteCssUrl='/spec/style.css' \
+						banner='This copy of the specification has test results interspersed throughout; click on ℹ️ to see them.'
+stylesheet := lib/rfcbootstrap.xslt
+
+specs := rfc9111
+TARGETS := $(addsuffix .html,$(specs))
+
+.PHONY: all clean
+
+all: $(TARGETS)
+
+clean:
+	rm -f $(TARGETS)
+
+%.cleanxml: %.xml
+	$(saxon) $< lib/xreffer.xslt | $(saxon) - lib/clean-for-DTD.xslt > $@
+
+%.html: %.cleanxml $(stylesheet)
+	$(saxon) $< $(stylesheet) $(saxparam) pageUrl='https://httpwg.org/specs/$@' > $@
diff --git a/test/fixtures/cache-tests/spec/bootstrap.min.css b/test/fixtures/cache-tests/spec/bootstrap.min.css
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/bootstrap.min.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v4.5.3 (https://getbootstrap.com/)
+ * Copyright 2011-2020 The Bootstrap Authors
+ * Copyright 2011-2020 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
+/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/lib/clean-for-DTD.xslt b/test/fixtures/cache-tests/spec/lib/clean-for-DTD.xslt
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/clean-for-DTD.xslt
@@ -0,0 +1,2356 @@
+<!--
+    Strip rfc2629.xslt extensions, generating XML input for "official" xml2rfc
+
+    Copyright (c) 2006-2020, Julian Reschke (julian.reschke@greenbytes.de)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Julian Reschke nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="1.0"
+                xmlns:exslt="http://exslt.org/common"
+                xmlns:ed="http://greenbytes.de/2002/rfcedit"
+                xmlns:grddl="http://www.w3.org/2003/g/data-view#"
+                xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:x="http://purl.org/net/xml2rfc/ext"
+                xmlns:xi="http://www.w3.org/2001/XInclude"
+                xmlns:xhtml="http://www.w3.org/1999/xhtml"
+                exclude-result-prefixes="ed exslt grddl rdf svg x xi xhtml"
+>
+
+<!-- re-use some of the default RFC2629.xslt rules -->
+<xsl:import href="rfc2629-no-doctype.xslt"/>
+
+<!-- undo strip-space decls -->
+<xsl:preserve-space elements="*"/>
+
+<!-- generate UTF-8 XML with no doctype decl and artwork/sourcecode serialized as CDATA -->
+<xsl:output method="xml" version="1.0" encoding="UTF-8" cdata-section-elements="artwork sourcecode" />
+
+<!-- Workaround for http://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/297 -->
+<xsl:param name="xml2rfc-ext-strip-vbare">false</xsl:param>
+
+<!-- xml2rfc target -->
+<xsl:param name="xml2rfc-ext-xml2rfc-backend">
+  <xsl:variable name="default">
+    <xsl:choose>
+      <xsl:when test="$pub-yearmonth &gt;= 201705">201706</xsl:when>
+      <xsl:when test="$pub-yearmonth &gt; 201612">201610</xsl:when>
+      <xsl:otherwise>201510</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'xml2rfc-backend'"/>
+    <xsl:with-param name="default" select="$default"/>
+  </xsl:call-template>
+</xsl:param>
+<xsl:param name="xml2rfc-ext-xml2rfc-voc">2</xsl:param>
+
+<!-- kick into cleanup mode -->
+<xsl:template match="/">
+  <xsl:text>&#10;</xsl:text>
+  <xsl:comment>
+    This XML document is the output of clean-for-DTD.xslt; a tool that strips
+    extensions to RFC 7749 from documents for processing with xml2rfc.
+</xsl:comment>
+<xsl:text>&#10;</xsl:text>
+<xsl:comment>TARGET-GENERATOR: <xsl:value-of select="$xml2rfc-ext-xml2rfc-backend"/></xsl:comment>
+<xsl:text>&#10;</xsl:text>
+<xsl:comment>TARGET-VOCABULARY: <xsl:value-of select="$xml2rfc-ext-xml2rfc-voc"/></xsl:comment>
+  <xsl:apply-templates select="/" mode="cleanup"/>
+</xsl:template>
+
+<!-- rules for identity transformations -->
+
+<xsl:template match="processing-instruction()" mode="cleanup">
+  <xsl:text>&#10;</xsl:text>
+  <xsl:copy/>
+</xsl:template>
+
+<xsl:template match="comment()|@*" mode="cleanup"><xsl:copy/></xsl:template>
+
+<xsl:template match="text()" mode="cleanup"><xsl:copy/></xsl:template>
+
+<xsl:template match="/" mode="cleanup">
+	<xsl:copy><xsl:apply-templates select="node()" mode="cleanup" /></xsl:copy>
+</xsl:template>
+
+<xsl:template match="*" mode="cleanup">
+  <xsl:element name="{local-name()}">
+  	<xsl:apply-templates select="node()|@*" mode="cleanup" />
+  </xsl:element>
+</xsl:template>
+
+
+<!-- remove PI extensions -->
+
+<xsl:template match="processing-instruction('rfc-ext')" mode="cleanup"/>
+<xsl:template match="processing-instruction('BEGININC')" mode="cleanup"/>
+<xsl:template match="processing-instruction('ENDINC')" mode="cleanup"/>
+
+<!-- process include PI -->
+<xsl:template match="processing-instruction('rfc')" mode="cleanup">
+  <xsl:variable name="include">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="."/>
+      <xsl:with-param name="attr" select="'include'"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$include=''">
+      <xsl:text>&#10;</xsl:text>
+      <xsl:copy/>
+    </xsl:when>
+    <xsl:when test="substring($include, string-length($include) - 3) != '.xml'">
+      <xsl:apply-templates select="document(concat($include,'.xml'))" mode="cleanup"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates select="document($include)" mode="cleanup"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+
+<!-- add issues appendix -->
+
+<xsl:template match="back" mode="cleanup">
+  <back>
+    <xsl:apply-templates select="node()|@*" mode="cleanup" />
+    <xsl:if test="not(/*/@ed:suppress-issue-appendix='yes') and //ed:issue[@status='closed']">
+      <section title="Resolved issues (to be removed by RFC Editor before publication)">
+        <t>
+          Issues that were either rejected or resolved in this version of this
+          document.
+        </t>
+        <xsl:apply-templates select="//ed:issue[@status='closed']" mode="issues" />
+      </section>
+    </xsl:if>
+    <xsl:if test="not(/*/@ed:suppress-issue-appendix='yes') and //ed:issue[@status='open']">
+      <section title="Open issues (to be removed by RFC Editor prior to publication)">
+        <xsl:apply-templates select="//ed:issue[@status!='closed']" mode="issues" />
+      </section>
+    </xsl:if>
+  </back>
+</xsl:template>
+
+
+<!-- V3 features -->
+
+<xsl:template match="boilerplate" mode="cleanup"/>
+<xsl:template match="link" mode="cleanup"/>
+<xsl:template match="rfc/@scripts" mode="cleanup"/>
+<xsl:template match="rfc/@version" mode="cleanup">
+  <xsl:if test="$xml2rfc-ext-xml2rfc-voc >= 3">
+    <xsl:copy-of select="."/>
+  </xsl:if>
+</xsl:template>
+<xsl:template match="@pn" mode="cleanup"/>
+
+<xsl:template match="br" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <br>
+        <xsl:apply-templates select="node()|@*" mode="cleanup" />
+      </br>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text> </xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:u-map" mode="cleanup"/>
+<xsl:template match="u" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <u>
+        <xsl:apply-templates select="node()|@*" mode="cleanup" />
+      </u>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="emit-u"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- experimental for QUIC tls draft -->
+<xsl:template match="t/contact" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <contact>
+        <xsl:apply-templates select="node()|@*" mode="cleanup" />
+      </contact>
+    </xsl:when>
+    <xsl:when test="@asciiFullname">
+      <xsl:value-of select="@asciiFullname"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="@fullname"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- extensions -->
+
+<xsl:template match="x:abnf-char-sequence" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="substring(.,1,1) != '&quot;' or substring(.,string-length(.),1) != '&quot;'">
+      <xsl:call-template name="error">
+        <xsl:with-param name="inline">no</xsl:with-param>
+        <xsl:with-param name="msg" select="'contents of x:abnf-char-sequence needs to be quoted.'" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>%x</xsl:text>
+      <xsl:call-template name="to-abnf-char-sequence">
+        <xsl:with-param name="chars" select="substring(.,2,string-length(.)-2)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:anchor-alias" mode="cleanup"/>
+
+<xsl:template match="x:bcp14|bcp14" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <bcp14>
+        <xsl:apply-templates mode="cleanup"/>
+      </bcp14>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:assign-section-number" mode="cleanup"/>  
+<xsl:template match="x:link" mode="cleanup"/>
+<xsl:template match="x:source" mode="cleanup"/>
+<xsl:template match="x:feedback" mode="cleanup"/>
+<xsl:template match="date/@x:include-day" mode="cleanup"/>
+
+<xsl:template match="x:parse-xml" mode="cleanup">
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="x:prose" mode="cleanup">
+  <xsl:variable name="text" select="."/>
+  <xsl:comment>Converted from rfc2629.xslt x:prose extension</xsl:comment>
+  <xsl:choose>
+    <xsl:when test="contains($text,' ')">
+      <seriesInfo name="{substring-before($text,' ')}" value="{substring-after($text,' ')}"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <seriesInfo name="" value="{$text}"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="t/@keepWithNext|t/@keepWithPrevious" mode="cleanup"/>
+
+<xsl:template match="refcontent" mode="cleanup">
+  <xsl:variable name="text">
+    <xsl:apply-templates mode="cleanup"/>
+  </xsl:variable>
+  <xsl:comment>Converted from rfc2629.xslt refcontent extension</xsl:comment>
+  <xsl:choose>
+    <xsl:when test="contains($text,' ')">
+      <seriesInfo name="{substring-before($text,' ')}" value="{substring-after($text,' ')}"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <seriesInfo name="" value="{$text}"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="postalLine" mode="cleanup">
+  <xsl:comment>converted from v3 &lt;postalLine&gt;</xsl:comment>
+  <street><xsl:value-of select="."/></street>
+</xsl:template>
+
+<xsl:template match="x:ref" mode="cleanup">
+  <xsl:variable name="val" select="normalize-space(.)"/>
+  <xsl:variable name="target" select="//*[@anchor and (@anchor=$val or x:anchor-alias/@value=$val)][not(ancestor::ed:del)] | //reference/x:source[x:defines=$val]"/>
+  <xsl:if test="count($target)>1">
+    <xsl:message terminate="yes">FATAL: multiple x:ref targets found for <xsl:value-of select="$val"/>.</xsl:message>
+  </xsl:if>
+  <xsl:choose>
+    <xsl:when test="$target/self::x:source">
+      <!-- drop it-->
+      <xsl:value-of select="."/>
+    </xsl:when>
+    <xsl:when test="$target">
+      <xsl:variable name="current" select="."/>
+      <xsl:for-each select="$target">
+        <!-- make it the context -->
+        <xsl:choose>
+          <xsl:when test="self::preamble">
+            <!-- it's not an element we can link to -->
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">couldn't create the link as <xsl:value-of select="name()"/> does not support the anchor attribute.</xsl:with-param>
+            </xsl:call-template>
+            <xsl:value-of select="$current"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xref target="{$target/@anchor}" format="none"><xsl:value-of select="$current"/></xref>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="//x:source">
+      <xsl:variable name="ref" select="."/>
+      <xsl:variable name="out">
+        <!-- try referenced documents one by one -->
+        <xsl:for-each select="//reference[x:source]">
+          <xsl:variable name="extdoc" select="document(x:source/@href)"/>
+          <xsl:variable name="nodes" select="$extdoc//*[@anchor and (x:anchor-alias/@value=$val)]"/>
+          <xsl:choose>
+            <xsl:when test="not($nodes)">
+              <xsl:call-template name="trace">
+                <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' not found in source file '<xsl:value-of select="x:source/@href"/>'.</xsl:with-param>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:call-template name="info">
+                <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' found in source file '<xsl:value-of select="x:source/@href"/>'.</xsl:with-param>
+              </xsl:call-template>
+              <xsl:value-of select="$ref"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:copy-of select="$out"/>
+      <xsl:if test="string-length($out)=0">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' not found anywhere in references.</xsl:with-param>
+        </xsl:call-template>
+        <xsl:value-of select="$val"/>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">internal link target for '<xsl:value-of select="$val"/>' does not exist.</xsl:with-param>
+      </xsl:call-template>
+      <xsl:value-of select="."/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:blockquote|blockquote" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <blockquote>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </blockquote>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="blockquote-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="blockquote-to-v2">
+  <t>
+    <xsl:apply-templates select="@anchor" mode="cleanup"/>
+    <list>
+      <xsl:choose>
+        <xsl:when test="t|ul|ol|dl|artwork|figure|sourcecode">
+          <xsl:apply-templates mode="cleanup" />
+        </xsl:when>
+        <xsl:otherwise>
+          <t>
+            <xsl:apply-templates mode="cleanup" />
+          </t>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:if test="@quotedFrom">
+        <t>
+          <xsl:text>&#8212; </xsl:text>
+          <xsl:choose>
+            <xsl:when test="@cite"><eref target="{@cite}"><xsl:value-of select="@quotedFrom"/></eref></xsl:when>
+            <xsl:otherwise><xsl:value-of select="@quotedFrom"/></xsl:otherwise>
+          </xsl:choose>
+        </t>
+      </xsl:if>
+    </list>
+  </t>
+</xsl:template>
+
+<xsl:template match="li/blockquote" mode="cleanup">
+  <list style="empty">
+    <xsl:choose>
+      <xsl:when test="t|ul|ol|dl|artwork|figure|sourcecode">
+        <xsl:apply-templates mode="cleanup" />
+      </xsl:when>
+      <xsl:otherwise>
+        <t>
+          <xsl:apply-templates mode="cleanup" />
+        </t>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="@quotedFrom">
+      <t>
+        <xsl:text>&#8212; </xsl:text>
+        <xsl:choose>
+          <xsl:when test="@cite"><eref target="{@cite}"><xsl:value-of select="@quotedFrom"/></eref></xsl:when>
+          <xsl:otherwise><xsl:value-of select="@quotedFrom"/></xsl:otherwise>
+        </xsl:choose>
+      </t>
+    </xsl:if>
+  </list>
+</xsl:template>
+
+<xsl:template match="x:h" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <strong>
+        <xsl:apply-templates mode="cleanup"/>
+      </strong>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates mode="cleanup" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:highlight" mode="cleanup">
+  <xsl:apply-templates mode="cleanup" />
+</xsl:template>
+
+<xsl:template match="x:lt" mode="cleanup">
+  <t>
+    <xsl:apply-templates select="@hangText|@anchor" mode="cleanup"/>
+    <xsl:for-each select="t">
+      <xsl:apply-templates mode="cleanup"/>
+      <xsl:if test="position()!=last()">
+        <vspace blankLines="1"/>
+      </xsl:if>
+    </xsl:for-each>
+  </t>
+</xsl:template>
+
+<xsl:template match="x:note|aside" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <aside>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </aside>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="aside-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="aside-to-v2">
+  <t>
+    <xsl:apply-templates select="@anchor" mode="cleanup"/>
+    <list>
+      <xsl:apply-templates mode="cleanup"/>
+    </list>
+  </t>
+</xsl:template>
+
+<xsl:template match="x:q" mode="cleanup">
+  <xsl:text>"</xsl:text>
+  <xsl:apply-templates mode="cleanup"/>
+  <xsl:text>"</xsl:text>
+</xsl:template>
+
+<xsl:template match="x:dfn" mode="cleanup">
+  <!-- help xml2rfc to keep dfn and following text on the same page -->
+  <!-- removed for now because it broke httpbis-p2 (def of 200 OK in -25)
+  <xsl:if test="not(preceding-sibling::x:dfn) and count(following-sibling::list)=1 and normalize-space(../text()='')">
+    <xsl:processing-instruction name="rfc">needLines="4"</xsl:processing-instruction>
+  </xsl:if>-->
+  <xsl:apply-templates mode="cleanup"/>
+</xsl:template>
+
+<xsl:template match="x:sup|sup" mode="cleanup">
+  <xsl:text>^</xsl:text>
+  <xsl:apply-templates mode="cleanup" />
+</xsl:template>
+
+<xsl:template match="sub" mode="cleanup">
+  <xsl:text>_</xsl:text>
+  <xsl:apply-templates mode="cleanup" />
+</xsl:template>
+
+<xsl:template match="x:span" mode="cleanup">
+  <xsl:apply-templates mode="cleanup" />
+</xsl:template>
+<xsl:template match="x:span/@anchor" mode="cleanup"/>
+
+<xsl:template match="author/@asciiFullname" mode="cleanup"/>
+<xsl:template match="author/@asciiInitials" mode="cleanup"/>
+<xsl:template match="author/@asciiSurname" mode="cleanup"/>
+
+<xsl:template match="author/@surname" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="../@asciiSurname!=''">
+      <xsl:attribute name="surname"><xsl:value-of select="../@asciiSurname"/></xsl:attribute>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Replacing surname <xsl:value-of select="../@surname"/> by <xsl:value-of select="../@asciiSurname"/>.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise><xsl:copy/></xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="author/@fullname" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="../@asciiFullname!=''">
+      <xsl:attribute name="fullname"><xsl:value-of select="../@asciiFullname"/></xsl:attribute>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Replacing fullname <xsl:value-of select="../@fullname"/> by <xsl:value-of select="../@asciiFullname"/>.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise><xsl:copy/></xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="author/@initials" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="../@asciiInitials!=''">
+      <xsl:attribute name="initials"><xsl:value-of select="../@asciiInitials"/></xsl:attribute>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Replacing initials <xsl:value-of select="../@initials"/> by <xsl:value-of select="../@asciiInitials"/>.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise><xsl:copy/></xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="author/@anchor" mode="cleanup"/>
+<xsl:template match="x:include-author" mode="cleanup">
+  <t>
+    <xsl:value-of select="/*/front/author[@anchor=current()/@target]"/>
+  </t>
+  <t>
+    (see Authors Section)
+  </t>
+</xsl:template>
+
+<xsl:template match="organization/@ascii" mode="cleanup"/>
+<xsl:template match="organization" mode="cleanup">
+  <organization>
+    <xsl:apply-templates select="@*" mode="cleanup"/>
+    <xsl:choose>
+      <xsl:when test="@ascii!=''">
+        <xsl:value-of select="@ascii"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="text()"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </organization>
+</xsl:template>
+
+<xsl:template match="title/@ascii" mode="cleanup"/>
+<xsl:template match="title" mode="cleanup">
+  <title>
+    <xsl:apply-templates select="@*" mode="cleanup"/>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+        <xsl:apply-templates select="node()" mode="cleanup"/>
+      </xsl:when>
+      <xsl:when test="@ascii!=''">
+        <xsl:value-of select="@ascii"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:for-each select="node()">
+          <xsl:choose>
+            <xsl:when test="self::br">
+              <xsl:text> </xsl:text>
+            </xsl:when>
+            <xsl:when test="self::*">
+              <xsl:apply-templates select="node()" mode="cleanup"/>
+            </xsl:when>
+            <xsl:when test="self::processing-instruction()"/>
+            <xsl:otherwise>
+              <xsl:value-of select="normalize-space(.)"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:otherwise>
+    </xsl:choose>
+  </title>
+</xsl:template>
+
+<xsl:template match="@x:optional-ascii" mode="cleanup"/>
+<xsl:template match="@ascii" mode="cleanup"/>
+<xsl:template match="postal/*[@ascii or @x:optional-ascii]" mode="cleanup">
+  <xsl:element name="{local-name()}">
+    <xsl:apply-templates select="@*" mode="cleanup"/>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+        <xsl:copy-of select="@ascii"/>
+        <xsl:if test="@x:optional-ascii and not(@ascii)">
+          <!-- workaround for https://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/443 -->
+          <xsl:attribute name="ascii"><xsl:value-of select="@x:optional-ascii"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="text()"/>
+      </xsl:when>
+      <xsl:when test="@ascii!=''">
+        <xsl:value-of select="@ascii"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="text()"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:element>
+</xsl:template>
+
+<xsl:template match="postal" mode="cleanup">
+  <postal>
+    <xsl:apply-templates select="@*" mode="cleanup"/>
+    <xsl:if test="not(street) and not(postalLine)">
+      <!-- street is mandatory in V2 -->
+      <street/>
+    </xsl:if>
+    <xsl:apply-templates select="node()" mode="cleanup"/>
+  </postal>
+</xsl:template>
+
+<!-- not supported -->
+<xsl:template match="relref/@format" mode="cleanup"/>
+
+<xsl:template match="xref[(@x:fmt or @x:sec or @x:rel or @section or @sectionFormat or @relative) and not(*|text())]|relref[not(*|text())]" mode="cleanup">
+  <xsl:call-template name="insert-iref-for-xref"/>
+  <xsl:variable name="is-xref" select="self::xref"/>
+  <xsl:variable name="node" select="$src//*[@anchor=current()/@target]" />
+
+  <xsl:variable name="ssec">
+    <xsl:call-template name="get-section-xref-section"/>
+  </xsl:variable>
+
+  <xsl:variable name="tsec">
+    <xsl:choose>
+      <xsl:when test="starts-with(@x:rel,'#') and $ssec='' and $node/x:source/@href">
+        <xsl:variable name="extdoc" select="document($node/x:source/@href)"/>
+        <xsl:for-each select="$extdoc//*[@anchor=substring-after(current()/@x:rel,'#')]">
+          <xsl:variable name="t">
+            <xsl:call-template name="get-section-number"/>
+          </xsl:variable>
+          <xsl:choose>
+            <xsl:when test="starts-with($t,$unnumbered)">
+              <xsl:choose>
+                <xsl:when test="ancestor::back">A@</xsl:when>
+                <xsl:otherwise>S@</xsl:otherwise>
+              </xsl:choose>
+              <xsl:call-template name="get-title-as-string">
+                <xsl:with-param name="node" select="."/>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$t"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$ssec"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="sec">
+    <xsl:choose>
+      <xsl:when test="contains($tsec,'@')">"<xsl:value-of select="substring-after($tsec,'@')"/>"</xsl:when>
+      <xsl:otherwise><xsl:value-of select="$tsec"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="secterm">
+    <xsl:choose>
+      <!-- starts with letter or unnumbered? -->
+      <xsl:when test="translate(substring($sec,1,1),$ucase,'')='' or starts-with($tsec,'A@')">Appendix</xsl:when>
+      <xsl:otherwise>Section</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="sfmt">
+    <xsl:call-template name="get-section-xref-format">
+      <xsl:with-param name="default">
+        <xsl:choose>
+          <xsl:when test="ancestor::artwork or ancestor::sourcecode">comma</xsl:when>
+          <xsl:otherwise>of</xsl:otherwise>
+        </xsl:choose>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:variable>
+  
+  <!--<xsl:comment><xsl:value-of select="concat($sfmt, ' ', $tsec, ' ', @x:sec)"/></xsl:comment>-->
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3 and $tsec!='' and not(contains($tsec,'@')) and $sfmt='of'">
+      <xref target="{@target}" section="{$tsec}">
+        <xsl:if test="@x:rel">
+          <xsl:attribute name="relative"><xsl:value-of select="@x:rel"/></xsl:attribute>
+        </xsl:if>
+      </xref>
+    </xsl:when>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3 and $tsec!='' and not(contains($tsec,'@')) and $sfmt='comma'">
+      <xref target="{@target}" sectionFormat="comma" section="{$tsec}">
+        <xsl:if test="@x:rel">
+          <xsl:attribute name="relative"><xsl:value-of select="@x:rel"/></xsl:attribute>
+        </xsl:if>
+      </xref>
+    </xsl:when>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3 and $tsec!='' and not(contains($tsec,'@')) and $sfmt='bare'">
+      <xref target="{@target}" sectionFormat="bare" section="{$tsec}">
+        <xsl:if test="@x:rel">
+          <xsl:attribute name="relative"><xsl:value-of select="@x:rel"/></xsl:attribute>
+        </xsl:if>
+      </xref>
+    </xsl:when>
+    <xsl:when test="$sfmt='comma'">
+      <xref>
+        <xsl:apply-templates select="@target|@format|@pageno|text()|*" mode="cleanup"/>
+      </xref>
+      <xsl:text>, </xsl:text>
+      <xsl:value-of select="$secterm"/>
+      <xsl:text> </xsl:text>
+      <xsl:value-of select="$sec"/>
+    </xsl:when>
+    <xsl:when test="$sfmt='section'">
+      <xsl:value-of select="$secterm"/>
+      <xsl:text> </xsl:text>
+      <xsl:value-of select="$sec"/>
+    </xsl:when>
+    <xsl:when test="$sfmt='bare'">
+      <xsl:value-of select="$sec"/>
+    </xsl:when>
+    <xsl:when test="$sfmt='parens'">
+      <xref>
+        <xsl:apply-templates select="@target|@format|@pageno|text()|*" mode="cleanup"/>
+      </xref>
+      <xsl:text> (</xsl:text>
+      <xsl:value-of select="$secterm"/>
+      <xsl:text> </xsl:text>
+      <xsl:value-of select="$sec"/>
+      <xsl:text>)</xsl:text>
+    </xsl:when>
+    <xsl:when test="$sfmt='of'">
+      <xsl:value-of select="$secterm"/>
+      <xsl:text> </xsl:text>
+      <xsl:value-of select="$sec"/>
+      <xsl:text> of </xsl:text>
+      <xref>
+        <xsl:apply-templates select="@target|@format|@pageno|text()|*" mode="cleanup"/>
+      </xref>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:copy>
+        <xsl:apply-templates select="node()" mode="cleanup"/>
+      </xsl:copy>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="abstract/@anchor" mode="cleanup"/>
+<xsl:template match="note/@anchor" mode="cleanup"/>
+
+<xsl:template match="xref[(@x:fmt or @x:sec or @x:rel) and (*|text())]|relref[*|text()]" mode="cleanup">
+  <xsl:call-template name="insert-iref-for-xref"/>
+  <xsl:choose>
+    <xsl:when test="self::relref">
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:when>
+    <xsl:when test="@x:fmt='none'">
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:when>
+    <xsl:when test="not(@x:fmt)">
+      <xref>
+        <xsl:copy-of select="@target|@format"/>
+        <xsl:apply-templates mode="cleanup"/>
+      </xref>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:message>Unsupported x:fmt attribute.</xsl:message>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="xref[(text()|*) and (@target=//abstract/@anchor or @target=//note/@anchor or @target=//preamble/@anchor or @target=//spanx/@anchor or @target=//name//@anchor or @target=//references/@anchor or @target=//artwork/@anchor or @target=//sourcecode/@anchor or @target=//artset/@anchor)]" mode="cleanup">
+  <!-- remove the link -->
+  <xsl:apply-templates select="node()" mode="cleanup"/>
+</xsl:template>
+
+<xsl:template match="xref[(text()|*) and @format='none' and (@target=//artwork//*/@anchor or @target=//sourcecode//*/@anchor)]" mode="cleanup">
+  <!-- remove links to elements inside <artwork> or <sourcecode> -->
+  <xsl:apply-templates select="node()" mode="cleanup"/>
+</xsl:template>
+
+<xsl:template match="xref[not((text()|*)) and (@target=//abstract/@anchor or @target=//note/@anchor or @target=//preamble/@anchor or @target=//spanx/@anchor or @target=//references/@anchor or @target=//artwork/@anchor or @target=//sourcecode/@anchor or @target=//artset/@anchor)]" mode="cleanup">
+  <xsl:variable name="content">
+    <xsl:apply-templates select="."/>
+  </xsl:variable>
+  <xsl:value-of select="$content"/>
+</xsl:template>
+
+<xsl:template match="xref[not((text()|*)) and (not(@format) or @format='default') and (@target=//section[@numbered='false']/@anchor)]" mode="cleanup">
+  <!-- link to unnumbered section -->
+  <xsl:copy>
+    <xsl:copy-of select="@target"/>
+    <xsl:variable name="content">
+      <xsl:apply-templates select="."/>
+    </xsl:variable>
+    <xsl:value-of select="$content"/>
+  </xsl:copy>
+</xsl:template>
+
+<xsl:template match="xref" mode="cleanup" priority="0">
+  <xsl:call-template name="insert-iref-for-xref"/>
+  <xref>
+    <xsl:apply-templates select="@target|@format" mode="cleanup"/>
+    <xsl:apply-templates mode="cleanup"/>
+  </xref>
+</xsl:template>
+
+<xsl:template name="insert-iref-for-xref">
+  <xsl:if test="$xml2rfc-ext-include-references-in-index='yesxxx' and $xml2rfc-ext-include-index='yes'">
+    <xsl:if test="@target=/rfc/back//reference/@anchor">
+      <iref item="{@target}"/>
+      <xsl:if test="@x:sec">
+        <xsl:choose>
+          <xsl:when test="translate(substring(@x:sec,1,1),$ucase,'')=''">
+            <iref item="{@target}" subitem="Appendix {@x:sec}"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <iref item="{@target}" subitem="Section {@x:sec}"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+
+<!-- workaround for https://tools.ietf.org/tools/ietfdb/ticket/2900 -->
+<xsl:template match="iref/comment()" mode="cleanup"/>
+
+<!-- drop index gen extension -->
+<xsl:template match="iref" mode="cleanup">
+  <xsl:if test="$xml2rfc-ext-include-index='yes'">
+    <iref>
+      <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+    </iref>
+  </xsl:if>
+</xsl:template>
+
+
+<!-- issue tracking extensions -->
+
+<xsl:template match="@xml:lang" mode="cleanup"/>
+<xsl:template match="@xml:lang" />
+
+<xsl:template match="ed:*" mode="cleanup"/>
+<xsl:template match="ed:*" />
+
+<xsl:template match="@ed:*" mode="cleanup"/>
+<xsl:template match="@ed:*" />
+
+<xsl:template match="ed:annotation" mode="cleanup" />
+
+<xsl:template match="ed:replace" mode="cleanup">
+  <xsl:apply-templates mode="cleanup" />
+</xsl:template>
+
+<xsl:template match="ed:replace">
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="ed:ins" mode="cleanup">
+  <xsl:apply-templates mode="cleanup"/>
+</xsl:template>
+
+<xsl:template match="ed:ins">
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="ed:issue" mode="issues">
+  <section title="{@name}">
+    <xsl:variable name="sec">
+      <xsl:call-template name="get-section-number"/>
+    </xsl:variable>
+
+    <xsl:if test="$sec!=''">
+      <t>
+        In Section <xsl:value-of select="$sec"/>:
+      </t>
+    </xsl:if>
+    
+    <t>
+      Type: <xsl:value-of select="@type" />
+    </t>
+    <xsl:if test="@href">
+      <t>
+        <!-- temp. removed because of xml2rfc's handling of erefs when producing TXT-->
+        <!--<eref target="{@href}" /> -->
+        <xsl:text>&lt;</xsl:text>
+        <xsl:value-of select="@href"/>
+        <xsl:text>></xsl:text>
+        <xsl:if test="@alternate-href">
+          <xsl:text>, &lt;</xsl:text>
+          <xsl:value-of select="@alternate-href"/>
+          <xsl:text>></xsl:text>
+        </xsl:if>
+      </t>
+    </xsl:if>
+    <xsl:for-each select="ed:item">
+      <t>
+        <xsl:if test="@entered-by or @date">
+          <xsl:choose>
+            <xsl:when test="not(@entered-by)">
+              <xsl:value-of select="concat('(',@date,') ')" />
+            </xsl:when>
+            <xsl:when test="not(@date)">
+              <xsl:value-of select="concat(@entered-by,': ')" />
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="concat(@entered-by,' (',@date,'): ')" />
+            </xsl:otherwise>
+          </xsl:choose>      
+        </xsl:if>
+        <xsl:if test="not(xhtml:p)">
+          <xsl:apply-templates select="node()" mode="issues"/>
+        </xsl:if>
+      </t>
+      <xsl:if test="xhtml:p|xhtml:pre">
+        <xsl:for-each select="node()">
+          <xsl:choose>
+            <xsl:when test="self::xhtml:p">
+              <t>
+                <xsl:apply-templates select="node()" mode="issues"/>
+              </t>
+            </xsl:when>
+            <xsl:when test="self::xhtml:pre">
+              <figure>
+                <artwork><xsl:apply-templates select="node()" mode="issues"/></artwork>
+              </figure>
+            </xsl:when>
+            <xsl:otherwise>
+              <t>
+                <xsl:apply-templates select="." mode="issues"/>
+              </t>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:if>
+    </xsl:for-each> 
+    <xsl:if test="ed:resolution">
+      <t>
+        <xsl:text>Resolution</xsl:text>
+        <xsl:if test="ed:resolution/@datetime"> (<xsl:value-of select="ed:resolution/@datetime"/>)</xsl:if>
+        <xsl:text>: </xsl:text>
+        <xsl:value-of select="ed:resolution" />
+      </t>
+    </xsl:if>
+  </section>
+</xsl:template>
+
+<xsl:template match="ed:issueref" mode="cleanup">
+  <xsl:apply-templates mode="cleanup"/>
+</xsl:template>
+
+<xsl:template match="*" mode="issues">
+  <xsl:apply-templates mode="issues"/>
+</xsl:template>
+
+<xsl:template match="xhtml:q" mode="issues">
+  <list><t>
+    <xsl:text>"</xsl:text>
+    <xsl:apply-templates mode="issues"/>
+    <xsl:text>"</xsl:text>
+    <xsl:if test="@cite">
+      <xsl:text> -- </xsl:text>
+      <eref target="{@cite}"><xsl:value-of select="@cite"/></eref>
+    </xsl:if>
+  </t></list>
+</xsl:template>
+
+<xsl:template match="xhtml:br" mode="issues">
+  <vspace/>
+</xsl:template>
+
+<xsl:template match="xhtml:del" mode="issues">
+  <xsl:text>&lt;del></xsl:text>
+    <xsl:apply-templates mode="issues"/>
+  <xsl:text>&lt;/del></xsl:text>
+</xsl:template>
+
+<xsl:template match="xhtml:em" mode="issues">
+  <spanx style="emph">
+    <xsl:apply-templates mode="issues"/>
+  </spanx>
+</xsl:template>
+
+<xsl:template match="xhtml:ins" mode="issues">
+  <xsl:text>&lt;ins></xsl:text>
+    <xsl:apply-templates mode="issues"/>
+  <xsl:text>&lt;/ins></xsl:text>
+</xsl:template>
+
+<xsl:template match="xhtml:tt" mode="issues">
+  <xsl:apply-templates mode="issues"/>
+</xsl:template>
+
+<xsl:template match="ed:eref" mode="issues">
+  <xsl:text>&lt;</xsl:text>
+  <xsl:value-of select="."/>
+  <xsl:text>&gt;</xsl:text>
+</xsl:template>
+
+<xsl:template match="ed:issueref" mode="issues">
+  <xsl:apply-templates mode="issues"/>
+</xsl:template>
+
+<xsl:template match="text()" mode="issues">
+  <xsl:value-of select="." />
+</xsl:template>
+
+<!-- workgroup format -->
+<xsl:template match="workgroup" mode="cleanup">
+  <workgroup>
+    <xsl:variable name="v" select="normalize-space(.)"/>
+    <xsl:variable name="h">
+      <!-- when a single name, append WG/RG postfix automatically -->
+      <xsl:choose>
+        <xsl:when test="not(contains($v, ' ')) and starts-with(/rfc/@docName,'draft-ietf-') and $submissionType='IETF'">
+          <xsl:value-of select="concat($v, ' Working Group')"/>
+        </xsl:when>
+        <xsl:when test="not(contains($v, ' ')) and starts-with(/rfc/@docName,'draft-irtf-') and $submissionType='IRTF'">
+          <xsl:value-of select="concat($v, ' Research Group')"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$v"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:value-of select="$h"/>
+  </workgroup>
+</xsl:template>
+
+<!-- markup inside artwork element -->
+
+<xsl:template match="figure" mode="cleanup">
+  <!-- move up iref elements -->
+  <xsl:for-each select=".//artwork//xref">
+    <xsl:if test="not(ancestor::ed:del)">
+      <xsl:call-template name="insert-iref-for-xref"/>
+    </xsl:if>
+  </xsl:for-each>
+  <figure>
+    <xsl:apply-templates select="@align|@alt|@anchor|@height|@src|@suppress-title|@width" mode="cleanup" />
+    <xsl:if test="not(@anchor) and artset/artwork/@anchor">
+      <!-- propagate anchor -->
+      <xsl:copy-of select="artset/artwork/@anchor[1]"/>
+    </xsl:if>
+    <xsl:variable name="title">
+      <xsl:choose>
+        <xsl:when test="name">
+          <xsl:variable name="hold">
+            <xsl:apply-templates select="name/node()"/>
+          </xsl:variable>
+          <xsl:value-of select="normalize-space($hold)"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@title"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$title!=''">
+      <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates select=".//artwork//iref|.//sourcecode//iref" mode="cleanup"/>
+    <xsl:apply-templates select="iref|preamble|artwork|artset|sourcecode|postamble|ed:replace|ed:ins|ed:del" mode="cleanup" />
+  </figure>
+</xsl:template>
+<xsl:template match="figure/name" mode="cleanup"/>
+
+<xsl:template name="insert-begin-code"/>
+<xsl:template name="insert-end-code"/>
+<xsl:template match="@x:is-code-component" mode="cleanup"/>
+
+<xsl:template match="artwork[svg:svg]" mode="cleanup">
+<xsl:call-template name="warning">
+  <xsl:with-param name="msg">SVG image removed.</xsl:with-param>
+</xsl:call-template>
+<artwork>(see SVG image in HTML version)</artwork>
+</xsl:template>
+
+<xsl:template match="artwork" mode="cleanup">
+  <xsl:call-template name="insert-markup"/>
+</xsl:template>
+
+<xsl:template match="artwork[not(ancestor::figure)]" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <xsl:apply-templates select=".//iref" mode="cleanup"/>
+      <xsl:call-template name="insert-markup"/>
+    </xsl:when>
+    <xsl:when test="parent::blockquote">
+      <t>
+        <xsl:call-template name="bare-artwork-to-v2"/>
+      </t>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="bare-artwork-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="bare-artwork-to-v2">
+  <figure>
+    <!-- propagate anchor -->
+    <xsl:if test="parent::artset and not(../@anchor)">
+      <xsl:copy-of select="@anchor"/>
+    </xsl:if>
+    <!-- move irefs up -->
+    <xsl:apply-templates select="iref" mode="cleanup"/>
+    <xsl:call-template name="insert-markup"/>
+  </figure>
+</xsl:template>
+
+<xsl:template match="artwork/@anchor" mode="cleanup"/>
+
+<xsl:template name="insert-markup">
+  <xsl:variable name="content2"><xsl:apply-templates select="node()"/></xsl:variable>
+  <xsl:variable name="content" select="translate($content2,'&#160;&#x2500;&#x2502;&#x2508;&#x250c;&#x2510;&#x2514;&#x2518;&#x251c;&#x2524;',' -|+++++++')"/>
+  <artwork>
+    <xsl:apply-templates select="@*" mode="cleanup" />
+    <xsl:if test="@x:is-code-component='yes'">
+      <xsl:if test="starts-with(.,'&#10;')">
+        <xsl:text>&#10;</xsl:text>
+      </xsl:if>
+      <xsl:value-of select="@x:indent-with"/>
+      <xsl:text>&lt;CODE BEGINS&gt;&#10;</xsl:text>
+    </xsl:if>
+    <xsl:if test="starts-with(.,'&#10;')">
+      <xsl:text>&#10;</xsl:text>
+      <xsl:value-of select="@x:indent-with"/>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="@x:indent-with!=''">
+        <xsl:call-template name="indent">
+          <xsl:with-param name="content" select="$content"/>
+          <xsl:with-param name="with" select="@x:indent-with"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$content"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="@x:is-code-component='yes'">&#10;&lt;CODE ENDS&gt;&#10;</xsl:if>
+  </artwork>  
+</xsl:template>
+
+<xsl:template match="@x:indent-with" mode="cleanup"/>
+<xsl:template match="@x:lang" mode="cleanup"/>
+
+<xsl:template name="indent">
+  <xsl:param name="content"/>
+  <xsl:param name="with"/>
+
+  <xsl:value-of select="substring($content,1,1)"/>
+  <xsl:if test="substring($content,1,1)='&#10;'">
+    <xsl:value-of select="$with"/>
+  </xsl:if>
+  
+  <xsl:choose>
+    <xsl:when test="$content=''" />
+    <xsl:otherwise>
+      <xsl:call-template name="indent">
+        <xsl:with-param name="content" select="substring($content,2)"/>
+        <xsl:with-param name="with" select="$with"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+  
+</xsl:template>
+
+<xsl:template match="artset" mode="cleanup">
+  <!-- see https://tools.ietf.org/html/draft-levkowetz-xml2rfc-v3-implementation-notes-08#section-3.1.1 -->
+  <xsl:choose>
+    <xsl:when test="artwork[not(svg:svg or normalize-space(.)='' or @src!='')]">
+      <xsl:apply-templates select="artwork[not(svg:svg or normalize-space(.)='' or @src!='')][1]" mode="cleanup"/>
+    </xsl:when>
+    <xsl:when test="artwork">
+      <xsl:apply-templates select="artwork[1]" mode="cleanup"/>
+    </xsl:when>
+    <xsl:when test="not(artwork) and parent::figure">
+      <xsl:call-template name="error">
+        <xsl:with-param name="inline">no</xsl:with-param>
+        <xsl:with-param name="msg">artset needs to contain at least one artwork child element</xsl:with-param>
+      </xsl:call-template>
+      <artwork/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<!-- email repetitions -->
+<xsl:template match="email" mode="cleanup">
+  <!-- combine in a single element -->
+  <xsl:if test="not(preceding-sibling::email)">
+    <email>
+      <xsl:for-each select="../email">
+        <xsl:value-of select="."/>
+        <xsl:if test="position()!=last()">
+          <xsl:text>, </xsl:text>
+        </xsl:if>
+      </xsl:for-each>
+    </email>
+  </xsl:if>
+</xsl:template>
+
+<!-- defaults for <eref> brackets -->
+<xsl:template match="eref[not(*|text()) and not(ancestor::cref)]" mode="cleanup">
+  <eref>
+    <xsl:copy-of select="@target"/>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3 and not(@brackets)">
+        <xsl:attribute name="brackets">angle</xsl:attribute>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:copy-of select="@brackets"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </eref>
+</xsl:template>
+
+<!-- cref/@display -->
+<xsl:template match="cref/@display" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <xsl:copy-of select="."/>
+    </xsl:when>
+    <xsl:otherwise>
+      <!-- otherwise just drop -->
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- markup inside cref -->
+<xsl:template match="cref//eref" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <xsl:copy>
+        <xsl:apply-templates select="node()|@*" mode="cleanup"/>
+      </xsl:copy>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>&lt;</xsl:text>
+      <xsl:value-of select="@target"/>
+      <xsl:text>&gt;</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="cref//x:dfn|cref//x:ref" mode="cleanup" priority="9">
+  <xsl:variable name="text">
+    <xsl:apply-templates select="."/>
+  </xsl:variable>
+  <xsl:value-of select="$text"/>
+</xsl:template>
+
+<xsl:template match="cref//xref" mode="cleanup" priority="9">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <xsl:copy>
+        <xsl:apply-templates select="@*|*" mode="cleanup"/>
+      </xsl:copy>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="text">
+        <xsl:apply-templates select="."/>
+      </xsl:variable>
+      <xsl:value-of select="$text"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- annotations -->
+<xsl:template match="@x:annotation" mode="cleanup">
+  <xsl:comment>
+    <xsl:value-of select="."/>
+  </xsl:comment>
+  <xsl:call-template name="warning">
+    <xsl:with-param name="msg">Dropping annotation on <xsl:value-of select="local-name(..)"/> element.</xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<!-- artwork extensions -->
+<xsl:template match="artwork/@x:extraction-note" mode="cleanup"/>
+
+<!-- list formatting -->
+<xsl:template match="list/@x:indent" mode="cleanup"/>
+
+<!-- rewrite to 'hanging' for now -->
+<xsl:template match="list[@style='x:dictionary']" mode="cleanup">
+  <list style="hanging">
+    <xsl:copy-of select="@hangIndent"/>
+    <xsl:apply-templates select="*" mode="cleanup"/>
+  </list>
+</xsl:template>
+
+<!-- referencing extensions -->
+<xsl:template match="iref/@x:for-anchor" mode="cleanup"/>
+
+<!-- GRRDL info stripped -->
+<xsl:template match="@grddl:transformation" mode="cleanup"/>
+
+<!-- maturity level stripped -->
+<xsl:template match="@x:maturity-level" mode="cleanup"/>
+
+<!-- normativity stripped -->
+<xsl:template match="@x:nrm" mode="cleanup"/>
+
+<!-- table extensions -->
+<xsl:template match="texttable/@x:caption-side" mode="cleanup"/>
+
+<!-- title extensions -->
+<xsl:template match="title/@x:quotes" mode="cleanup"/>
+
+<!-- organization extensions -->
+<xsl:template match="organization/@showOnFrontPage" mode="cleanup"/>
+
+<!-- RDF info stripped -->
+<xsl:template match="rdf:*" mode="cleanup"/>
+
+<!-- cases where xml2rfc does not allow anchors -->
+<xsl:template match="c/@anchor" mode="cleanup"/>
+<xsl:template match="preamble/@anchor" mode="cleanup"/>
+<xsl:template match="spanx/@anchor" mode="cleanup"/>
+
+<!-- Workaround for http://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/297 -->
+<xsl:template match="spanx[@style='vbare']" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-strip-vbare='true'">
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <spanx style="vbare">
+        <xsl:apply-templates mode="cleanup"/>
+      </spanx>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- v3 features -->
+<xsl:template match="rfc/@sortRefs" mode="cleanup"/>
+<xsl:template match="rfc/@symRefs" mode="cleanup"/>
+<xsl:template match="rfc/@tocInclude" mode="cleanup"/>
+<xsl:template match="rfc/@tocDepth" mode="cleanup"/>
+<xsl:template match="rfc/@consensus" mode="cleanup"/>
+
+<!-- handled below -->
+<xsl:template match="rfc/@category" mode="cleanup"/>
+<xsl:template match="rfc/@ipr" mode="cleanup"/>
+
+<xsl:template match="rfc" mode="cleanup">
+  <xsl:if test="@sortRefs='true'">
+    <xsl:processing-instruction name="rfc">sortrefs="yes"</xsl:processing-instruction>
+  </xsl:if>
+  <xsl:if test="@symRefs='false'">
+    <xsl:processing-instruction name="rfc">symrefs="no"</xsl:processing-instruction>
+  </xsl:if>
+  <xsl:if test="$parsedTocDepth!=3 and $xml2rfc-ext-xml2rfc-voc &lt; 3">
+    <xsl:processing-instruction name="rfc">tocdepth="<xsl:value-of select="$parsedTocDepth"/>"</xsl:processing-instruction>
+  </xsl:if>
+  <xsl:if test="@version and (not(@tocInclude) or @tocInclude='true')">
+    <xsl:processing-instruction name="rfc">toc="yes"</xsl:processing-instruction>
+  </xsl:if>
+  <rfc>
+    <xsl:if test="not(@version) and $xml2rfc-ext-xml2rfc-voc >= 3">
+      <xsl:attribute name="version"><xsl:value-of select="$xml2rfc-ext-xml2rfc-voc"/></xsl:attribute>
+    </xsl:if>
+    <xsl:if test="not(@tocDepth) and $xml2rfc-ext-xml2rfc-voc >= 3 and $parsedTocDepth!=3">
+      <xsl:attribute name="tocDepth"><xsl:value-of select="$parsedTocDepth"/></xsl:attribute>
+    </xsl:if>
+    <xsl:if test="not(@indexInclude) and $xml2rfc-ext-xml2rfc-voc >= 3">
+      <!-- index gen broken in xml2rfc v3 mode for now, see https://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/418 -->
+      <xsl:attribute name="indexInclude">false</xsl:attribute>
+    </xsl:if>
+    <xsl:if test="not(@sortRefs) and $xml2rfc-ext-xml2rfc-voc >= 3 and $xml2rfc-sortrefs='yes'">
+      <xsl:attribute name="sortRefs">true</xsl:attribute>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="@consensus='yes' and $xml2rfc-ext-xml2rfc-voc >= 3"><xsl:attribute name="consensus">true</xsl:attribute></xsl:when>
+      <xsl:when test="@consensus='no' and $xml2rfc-ext-xml2rfc-voc >= 3"><xsl:attribute name="consensus">false</xsl:attribute></xsl:when>
+      <xsl:when test="@consensus='true' and $xml2rfc-ext-xml2rfc-voc &lt; 3"><xsl:attribute name="consensus">yes</xsl:attribute></xsl:when>
+      <xsl:when test="@consensus='false' and $xml2rfc-ext-xml2rfc-voc &lt; 3"><xsl:attribute name="consensus">no</xsl:attribute></xsl:when>
+      <xsl:otherwise><xsl:copy-of select="@consensus"/></xsl:otherwise>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="@submissionType='IETF' and not(@category) and $xml2rfc-ext-xml2rfc-voc >= 3">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">defaulting /rfc/@category to "info" for xml2rfc v3</xsl:with-param>
+        </xsl:call-template>
+        <xsl:attribute name="category">info</xsl:attribute>
+      </xsl:when>
+      <xsl:otherwise><xsl:copy-of select="@category"/></xsl:otherwise>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="@submissionType='IETF' and not(@ipr) and $xml2rfc-ext-xml2rfc-voc >= 3">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">defaulting /rfc/@ipr to "trust200902" for xml2rfc v3</xsl:with-param>
+        </xsl:call-template>
+        <xsl:attribute name="ipr">trust200902</xsl:attribute>
+      </xsl:when>
+      <xsl:otherwise><xsl:copy-of select="@ipr"/></xsl:otherwise>
+    </xsl:choose>
+    <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+  </rfc>
+</xsl:template>
+
+<xsl:template match="strong" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <strong>
+        <xsl:apply-templates select="node()|@*" mode="cleanup" />
+      </strong>
+    </xsl:when>
+    <xsl:when test="*">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">strong not translated when including child elements</xsl:with-param>
+      </xsl:call-template>
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <spanx style="strong">
+        <xsl:apply-templates mode="cleanup"/>
+      </spanx>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="em" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <em>
+        <xsl:apply-templates select="node()|@*" mode="cleanup" />
+      </em>
+    </xsl:when>
+    <xsl:when test="*">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">em not translated when including child elements</xsl:with-param>
+      </xsl:call-template>
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <spanx style="emph">
+        <xsl:apply-templates mode="cleanup"/>
+      </spanx>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="tt" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <tt>
+        <xsl:apply-templates select="node()|@*" mode="cleanup" />
+      </tt>
+    </xsl:when>
+    <xsl:when test="*">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">tt not translated when they include child elements</xsl:with-param>
+      </xsl:call-template>
+      <xsl:apply-templates mode="cleanup"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <spanx style="verb">
+        <xsl:apply-templates mode="cleanup"/>
+      </spanx>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="references/@anchor" mode="cleanup"/>
+
+<!-- New reference attributes -->
+<xsl:template match="reference/@quoteTitle" mode="cleanup">
+  <xsl:if test="$xml2rfc-ext-xml2rfc-backend >= 201706">
+    <xsl:attribute name="quote-title"><xsl:value-of select="."/></xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="reference/front/abstract" mode="cleanup"/>
+
+<xsl:template match="referencegroup" mode="cleanup">
+  <reference anchor="{@anchor}">
+    <xsl:copy-of select="@target"/>
+    <xsl:if test="$xml2rfc-ext-xml2rfc-backend >= 201706">
+      <xsl:attribute name="quote-title">false</xsl:attribute>
+    </xsl:if>
+    <xsl:comment>...expanded &lt;referencegroup>...</xsl:comment>
+    <front>
+      <title>
+        <xsl:text>Consisting of: </xsl:text>
+        <xsl:variable xmlns:myns="mailto:julian.reschke@greenbytes.de?subject=rfc2629.xslt" name="included" select="exslt:node-set($includeDirectives)/myns:include[@in=generate-id(current())]/*[self::reference or self::referencegroup]"/>
+        <xsl:for-each select="reference|$included">
+          <xsl:value-of select="concat('[',@anchor,']')"/>
+          <xsl:choose>
+            <xsl:when test="position() &lt; last() - 1">, </xsl:when>
+            <xsl:when test="position() = last() - 1">, and </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </xsl:for-each>
+      </title>
+      <author/>
+      <date/>
+    </front>
+  </reference>
+  <xsl:apply-templates mode="cleanup"/>
+</xsl:template>
+
+<xsl:template match="reference" mode="cleanup">
+  <reference>
+    <xsl:apply-templates select="@anchor|@target|@quoteTitle" mode="cleanup"/>
+    <xsl:choose>
+      <xsl:when test="not(@target) and $xml2rfc-ext-link-rfc-to-info-page='yes' and seriesInfo[@name='BCP'] and starts-with(@anchor,'BCP')">
+        <xsl:variable name="uri">
+          <xsl:call-template name="compute-rfc-info-uri">
+            <xsl:with-param name="type" select="'bcp'"/>
+            <xsl:with-param name="no" select="seriesInfo[@name='BCP']/@value"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:attribute name="target"><xsl:value-of select="$uri"/></xsl:attribute>
+      </xsl:when>
+      <xsl:when test="not(@target) and $xml2rfc-ext-link-rfc-to-info-page='yes' and seriesInfo[@name='RFC']">
+        <xsl:variable name="uri">
+          <xsl:call-template name="compute-rfc-info-uri">
+            <xsl:with-param name="type" select="'rfc'"/>
+            <xsl:with-param name="no" select="seriesInfo[@name='RFC']/@value"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:attribute name="target"><xsl:value-of select="$uri"/></xsl:attribute>
+      </xsl:when>
+      <xsl:when test="not(@target) and $xml2rfc-ext-link-rfc-to-info-page='yes' and not(seriesInfo) and document(x:source/@href)/rfc/@number">
+        <xsl:variable name="uri">
+          <xsl:call-template name="compute-rfc-info-uri">
+            <xsl:with-param name="type" select="'rfc'"/>
+            <xsl:with-param name="no" select="document(x:source/@href)/rfc/@number"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:attribute name="target"><xsl:value-of select="$uri"/></xsl:attribute>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="front">
+        <xsl:apply-templates select="front" mode="cleanup"/>
+      </xsl:when>
+      <xsl:when test="x:source">
+        <xsl:variable name="d" select="document(x:source/@href)"/>
+        <xsl:comment>included from <xsl:value-of select="x:source/@href"/></xsl:comment>
+        <front>
+          <xsl:apply-templates select="$d/rfc/front/title" mode="cleanup"/>
+          <xsl:apply-templates select="$d/rfc/front/author" mode="cleanup"/>
+          <xsl:choose>
+            <xsl:when test="$d/rfc/front/date/@*">
+              <!-- any date info present? -->
+              <xsl:apply-templates select="$d/rfc/front/date" mode="cleanup"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <!-- let defaults apply -->
+              <date year="{$xml2rfc-ext-pub-year}" month="{$xml2rfc-ext-pub-month}"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </front>
+        <xsl:if test="not(seriesInfo) and document(x:source/@href)/rfc/@docName">
+          <seriesInfo name="Internet-Draft" value="{document(x:source/@href)/rfc/@docName}"/>
+        </xsl:if>
+        <xsl:if test="not(seriesInfo) and document(x:source/@href)/rfc/@number">
+          <seriesInfo name="RFC" value="{document(x:source/@href)/rfc/@number}"/>
+        </xsl:if>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+    <xsl:apply-templates select="seriesInfo|front/seriesInfo" mode="cleanup"/>
+
+    <!-- Insert DOI for RFCs -->
+    <xsl:variable name="doi">
+      <xsl:choose>
+        <xsl:when test="seriesInfo|front/seriesInfo">
+          <xsl:call-template name="compute-doi"/>
+        </xsl:when>
+        <xsl:when test="document(x:source/@href)/rfc/@number">
+          <xsl:call-template name="compute-doi">
+            <xsl:with-param name="rfc" select="document(x:source/@href)/rfc/@number"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$xml2rfc-ext-insert-doi='yes' and $doi!='' and not(seriesInfo[@name='DOI']|front/seriesInfo[@name='DOI'])">
+      <seriesInfo name="DOI" value="{$doi}"/>
+    </xsl:if>
+
+    <xsl:apply-templates select="*[not(self::front) and not(self::seriesInfo)]" mode="cleanup"/>
+  </reference>
+</xsl:template>
+
+<xsl:template match="seriesInfo" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="@name='Internet-Draft' and $rfcno > 7375">
+      <!-- special case in RFC formatting since 2015 -->
+      <seriesInfo name="Work in Progress," value="{@value}"/>
+    </xsl:when>
+    <xsl:when test="@name='DOI' and starts-with(@value,'10.17487/RFC') and $xml2rfc-ext-insert-doi='no'">
+      <xsl:call-template name="info">
+        <xsl:with-param name="msg">Removing DOI <xsl:value-of select="@value"/> from &lt;reference> element</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <seriesInfo name="{@name}" value="{@value}"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="date[ancestor::reference]" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="@year!='' or normalize-space(.)=''">
+      <date>
+        <xsl:apply-templates select="@*" mode="cleanup"/>
+      </date>
+    </xsl:when>
+    <xsl:otherwise>
+      <date year="{normalize-space(.)}"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="front" mode="cleanup">
+  <!-- silence certain xml2rfcv3 warning messages -->
+  <xsl:if test="$xml2rfc-ext-xml2rfc-backend >= 201706 and not(ancestor::reference)">
+    <xsl:if test="not(/rfc/@consensus)">
+      <xsl:text>&#10;</xsl:text>
+      <xsl:comment>see https://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/420</xsl:comment>
+      <xsl:text>&#10;</xsl:text>
+      <xsl:processing-instruction name="v3xml2rfc">silence="Warning: Setting consensus="true" for IETF STD document"</xsl:processing-instruction>
+    </xsl:if>
+    <xsl:if test="$xml2rfc-ext-xml2rfc-voc >= 3 and not(/rfc/@submissionType) and not ($is-rfc)">
+      <!-- issue to be raised -->
+      <xsl:text>&#10;</xsl:text>
+      <xsl:processing-instruction name="v3xml2rfc">silence="Warning: Expected a valid submissionType (stream) setting"</xsl:processing-instruction>
+    </xsl:if>
+    <xsl:if test="$xml2rfc-ext-xml2rfc-voc >= 3 and substring(/rfc/@docName, string-length(/rfc/@docName)-string-length('-latest')+1)='-latest'">
+      <xsl:text>&#10;</xsl:text>
+      <xsl:comment>see https://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/439</xsl:comment>
+      <xsl:text>&#10;</xsl:text>
+      <xsl:processing-instruction name="v3xml2rfc">silence="The 'docName' attribute of the &lt;rfc/> element"</xsl:processing-instruction>
+    </xsl:if>
+  </xsl:if>
+  <front>
+    <xsl:apply-templates select="title" mode="cleanup"/>
+    <xsl:if test="$xml2rfc-ext-xml2rfc-voc >= 3 and seriesInfo">
+      <xsl:apply-templates select="seriesInfo" mode="cleanup"/>
+    </xsl:if>
+    <xsl:apply-templates select="author" mode="cleanup"/>
+    <xsl:apply-templates select="date" mode="cleanup"/>
+    <xsl:if test="not(date)">
+      <!-- mandatory in v2 -->
+      <date/>
+    </xsl:if>
+    <xsl:apply-templates select="text()|node()[not(self::seriesInfo or self::title or self::author or self::date)]" mode="cleanup"/>
+  </front>
+</xsl:template>
+
+<!-- Note titles -->
+<xsl:template match="note" mode="cleanup">
+  <note>
+    <xsl:apply-templates select="@anchor" mode="cleanup"/>
+    <xsl:variable name="title">
+      <xsl:choose>
+        <xsl:when test="name">
+          <xsl:variable name="hold">
+            <xsl:apply-templates select="name/node()"/>
+          </xsl:variable>
+          <xsl:value-of select="normalize-space($hold)"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@title"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+    <xsl:if test="@removeInRFC='true' and (not(t) or t[1]!=$note-removeInRFC)">
+      <t><xsl:value-of select="$note-removeInRFC"/></t>
+    </xsl:if>
+    <xsl:apply-templates mode="cleanup"/>
+  </note>
+</xsl:template>
+<xsl:template match="note/name" mode="cleanup"/>
+
+<!-- References -->
+<xsl:template match="references" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="parent::back and count(../references) > 1 and $xml2rfc-ext-xml2rfc-voc >= 3">
+      <!-- insert top-level references section -->
+      <xsl:if test="not(preceding-sibling::references)">
+        <references>
+          <name>References</name>
+          <xsl:for-each select="../references">
+            <references>
+              <xsl:variable name="title">
+                <xsl:choose>
+                  <xsl:when test="name">
+                    <xsl:variable name="hold">
+                      <xsl:apply-templates select="name/node()"/>
+                    </xsl:variable>
+                    <xsl:value-of select="normalize-space($hold)"/>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:value-of select="@title"/>
+                  </xsl:otherwise>
+                </xsl:choose>
+              </xsl:variable>
+              <xsl:apply-templates select="@anchor|@toc" mode="cleanup"/>
+              <xsl:if test="not(name)">
+                <name><xsl:value-of select="$title"/></name>
+              </xsl:if>
+              <xsl:apply-templates select="*" mode="cleanup"/>
+            </references>
+          </xsl:for-each>
+        </references>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <references>
+        <xsl:variable name="title">
+          <xsl:choose>
+            <xsl:when test="name">
+              <xsl:variable name="hold">
+                <xsl:apply-templates select="name/node()"/>
+              </xsl:variable>
+              <xsl:value-of select="normalize-space($hold)"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="@title"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <xsl:apply-templates select="@anchor|@toc" mode="cleanup"/>
+        <xsl:choose>
+          <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3 and name">
+            <xsl:apply-templates select="name" mode="cleanup"/>
+          </xsl:when>
+          <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+            <name><xsl:value-of select="$title"/></name>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:if test="$title!=''">
+              <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+            </xsl:if>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:apply-templates mode="cleanup" select="node()[not(self::name)]"/>
+      </references>      
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- Section titles -->
+<xsl:template match="section" mode="cleanup">
+  <section>
+    <xsl:copy-of select="@anchor|@toc"/>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-xml2rfc-backend >= 201610">
+        <xsl:copy-of select="@numbered"/>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+        <xsl:apply-templates select="@title" mode="cleanup"/>
+        <xsl:if test="name">
+          <name>
+            <xsl:apply-templates select="name/node()" mode="cleanup"/>
+          </name>
+        </xsl:if>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:attribute name="title">
+          <xsl:call-template name="get-title-as-string"/>
+        </xsl:attribute>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="@removeInRFC='true' and (not(t) or t[1]!=$section-removeInRFC)">
+      <t><xsl:value-of select="$section-removeInRFC"/></t>
+    </xsl:if>
+    <xsl:apply-templates mode="cleanup"/>
+  </section>
+  <xsl:if test="(@numbered='no' or @numbered='false') and $xml2rfc-ext-xml2rfc-backend &lt; 201610">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">unnumbered sections not supported</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+<xsl:template match="section/name" mode="cleanup"/>
+
+<!-- Definition Lists -->
+<xsl:template match="dl" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <dl>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </dl>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="dl-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="dl-to-v2">
+  <xsl:choose>
+    <xsl:when test="parent::dd">
+      <xsl:call-template name="process-dl"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <t>
+        <xsl:call-template name="process-dl"/>
+      </t>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="process-dl">
+  <xsl:copy-of select="@anchor"/>
+  <xsl:variable name="newl" select="@newline"/>
+  <xsl:variable name="spac" select="@spacing"/>
+  <xsl:if test="parent::section">
+    <!-- avoid adding PIs into nested lists due to xml2rfc bug -->
+    <xsl:processing-instruction name="rfc">
+      <xsl:choose>
+        <xsl:when test="not($spac='compact')">subcompact='no'</xsl:when>
+        <xsl:otherwise>subcompact='yes'</xsl:otherwise>
+      </xsl:choose>
+    </xsl:processing-instruction>
+  </xsl:if>
+  <list style="hanging">
+    <xsl:variable name="indent" select="@indent"/>
+    <xsl:if test="number($indent)=$indent">
+      <xsl:attribute name="hangIndent"><xsl:value-of select="$indent"/></xsl:attribute>
+    </xsl:if>
+    <xsl:for-each select="dt">
+      <xsl:variable name="txt">
+        <xsl:apply-templates select="." mode="cleanup"/>
+      </xsl:variable>
+      <!-- TODO: check for more block-level elements -->
+      <xsl:variable name="desc" select="following-sibling::dd[1]"/>
+      <xsl:variable name="block-level-children" select="$desc/artwork | $desc/dl | $desc/figure | $desc/ol | $desc/sourcecode | $desc/t | $desc/table | $desc/ul"/>
+      <t hangText="{normalize-space($txt)}">
+        <xsl:choose>
+          <xsl:when test="@anchor">
+            <xsl:copy-of select="@anchor"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:copy-of select="$desc/@anchor"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:if test="$newl='true'">
+          <xsl:choose>
+            <xsl:when test="$block-level-children">
+              <vspace blankLines="1"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <vspace blankLines="0"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+        <xsl:apply-templates select="iref" mode="cleanup"/>
+        <xsl:choose>
+          <xsl:when test="$block-level-children">
+            <xsl:for-each select="$block-level-children">
+              <xsl:choose>
+                <xsl:when test="self::t">
+                  <xsl:apply-templates select="node()" mode="cleanup"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:apply-templates select="." mode="cleanup"/>
+                </xsl:otherwise>
+              </xsl:choose>
+              <xsl:if test="position()!=last()">
+                <xsl:choose>
+                  <xsl:when test="not($spac='compact')"><vspace blankLines="1"/></xsl:when>
+                  <xsl:otherwise><vspace blankLines="0"/></xsl:otherwise>
+                </xsl:choose>
+              </xsl:if>
+            </xsl:for-each>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates select="$desc/node()" mode="cleanup"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </t>
+    </xsl:for-each>
+  </list>
+</xsl:template>
+
+<!-- rewrite link target going to <dd> to use preceding <dt>'s anchor when present -->
+<xsl:template match="xref/@target[.=//dd/@anchor]" mode="cleanup">
+  <xsl:variable name="t" select="//dd[@anchor=current()]"/>
+  <xsl:variable name="p" select="$t/preceding-sibling::dt[1]"/>
+  <xsl:choose>
+    <xsl:when test="$p/@anchor">
+      <xsl:attribute name="target"><xsl:value-of select="$p/@anchor"/></xsl:attribute>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:attribute name="target"><xsl:value-of select="@target"/></xsl:attribute>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- List items -->
+<xsl:template match="li" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <li>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </li>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="li-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="li-to-v2">
+  <t>
+    <xsl:copy-of select="@anchor"/>
+    <xsl:apply-templates mode="cleanup"/>
+  </t>
+</xsl:template>
+
+<xsl:template match="li/t" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <t>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </t>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="li-t-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="li-t-to-v2">
+  <xsl:apply-templates mode="cleanup"/>
+  <xsl:if test="position()!=last()">
+    <vspace blankLines="1"/>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="li/ul" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <ul>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </ul>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="li-ul-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+  
+<xsl:template name="li-ul-to-v2">
+  <list style="symbols">
+    <xsl:apply-templates mode="cleanup"/>
+  </list>
+  <xsl:if test="position()!=last()">
+    <vspace blankLines="1"/>
+  </xsl:if>
+</xsl:template>
+
+<!-- Ordered Lists -->
+<xsl:template match="ol" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <ol>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </ol>
+    </xsl:when>
+    <xsl:when test="parent::li">
+      <xsl:call-template name="ol-to-v2"/>
+      <xsl:if test="position()!=last()">
+        <vspace blankLines="1"/>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <t>
+        <xsl:call-template name="ol-to-v2"/>
+      </t>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="ol-to-v2">
+  <xsl:copy-of select="@anchor"/>
+  <xsl:if test="@start and @start!='1'">
+    <xsl:call-template name="error">
+      <xsl:with-param name="inline">no</xsl:with-param>
+      <xsl:with-param name="msg">list start != 1 not supported</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="@group">
+    <xsl:call-template name="error">
+      <xsl:with-param name="inline">no</xsl:with-param>
+      <xsl:with-param name="msg">ol/@group not supported</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:variable name="style">
+    <xsl:choose>
+      <xsl:when test="not(@type) or @type='1'">numbers</xsl:when>
+      <xsl:when test="@type='a'">letters</xsl:when>
+      <xsl:when test="@type='A'">
+        <xsl:call-template name="error">
+          <xsl:with-param name="inline">no</xsl:with-param>
+          <xsl:with-param name="msg">ol/@type=<xsl:value-of select="@type"/> not supported (defaulting to 'a')</xsl:with-param>
+        </xsl:call-template>
+        <xsl:text>letters</xsl:text>
+      </xsl:when>
+      <xsl:when test="string-length(@type)>1">format <xsl:value-of select="@type"/></xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="error">
+          <xsl:with-param name="inline">no</xsl:with-param>
+          <xsl:with-param name="msg">ol/@type=<xsl:value-of select="@type"/> not supported (defaulting to '1')</xsl:with-param>
+        </xsl:call-template>
+        <xsl:text>numbers</xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <list style="{$style}">
+    <xsl:if test="@group">
+      <xsl:attribute name="counter"><xsl:value-of select="@group"/></xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates mode="cleanup"/>
+  </list>
+</xsl:template>
+
+<!-- Unordered Lists -->
+<xsl:template match="ul" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <ul>
+        <xsl:apply-templates select="@*" mode="cleanup"/>
+        <xsl:if test="not(li) and @x:when-empty">
+          <li>
+            <xsl:value-of select="@x:when-empty"/>
+          </li>
+        </xsl:if>
+        <xsl:apply-templates select="node()" mode="cleanup"/>
+      </ul>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="ul-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+<xsl:template match="ul/@x:when-empty" mode="cleanup"/>
+
+<xsl:template name="ul-to-v2">
+  <xsl:choose>
+    <xsl:when test="not(li) and @x:when-empty">
+      <t>
+        <xsl:value-of select="@x:when-empty"/>
+      </t>
+    </xsl:when>
+    <xsl:otherwise>
+      <t>
+        <xsl:choose>
+          <xsl:when test="@empty='true'">
+            <list style="empty">
+              <xsl:apply-templates mode="cleanup"/>
+            </list>
+          </xsl:when>
+          <xsl:otherwise>
+            <list style="symbols">
+              <xsl:apply-templates mode="cleanup"/>
+            </list>
+          </xsl:otherwise>
+        </xsl:choose>
+      </t>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-content-of-artwork">
+  <xsl:variable name="content2"><xsl:apply-templates select="node()"/></xsl:variable>
+  <xsl:variable name="content" select="translate($content2,'&#160;&#x2500;&#x2502;&#x2508;&#x250c;&#x2510;&#x2514;&#x2518;&#x251c;&#x2524;',' -|+++++++')"/>
+  <xsl:value-of select="$content"/>
+</xsl:template>
+
+<xsl:template name="insert-sourcecode-as-artwork">
+  <artwork>
+    <xsl:copy-of select="@type"/>
+    <xsl:if test="@markers='true'">
+      <xsl:text>&lt;CODE BEGINS></xsl:text>
+      <xsl:if test="self::sourcecode and @name">
+        <xsl:variable name="offending" select="translate(@name,concat($alnum,'-+.,;_~#'),'')"/>
+        <xsl:choose>
+          <xsl:when test="$offending!=''">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">illegal characters in @name attribute '<xsl:value-of select="@name"/>': '<xsl:value-of select="$offending"/>'</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:text> file "</xsl:text>
+            <xsl:value-of select="@name"/>
+            <xsl:text>"</xsl:text>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      <xsl:text>&#10;</xsl:text>
+    </xsl:if>
+
+    <xsl:if test="starts-with(.,'&#10;')">
+      <xsl:text>&#10;</xsl:text>
+      <xsl:value-of select="@x:indent-with"/>
+    </xsl:if>
+    <xsl:call-template name="get-content-of-artwork"/>
+    <xsl:if test="@markers='true'">&#10;&lt;CODE ENDS></xsl:if>
+  </artwork>
+</xsl:template>
+
+<!-- Source Code -->
+<xsl:template match="sourcecode" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <xsl:apply-templates select=".//iref" mode="cleanup"/>
+      <sourcecode>
+        <xsl:copy-of select="@*"/>
+        <xsl:call-template name="get-content-of-artwork"/>
+      </sourcecode>
+    </xsl:when>
+    <xsl:when test="parent::figure">
+      <xsl:call-template name="insert-sourcecode-as-artwork"/>
+    </xsl:when>
+    <xsl:when test="parent::blockquote">
+      <t>
+        <figure>
+          <xsl:apply-templates select=".//iref" mode="cleanup"/>
+          <xsl:call-template name="insert-sourcecode-as-artwork"/>
+        </figure>
+      </t>
+    </xsl:when>
+    <xsl:otherwise>
+      <figure>
+        <xsl:apply-templates select=".//iref" mode="cleanup"/>
+        <xsl:call-template name="insert-sourcecode-as-artwork"/>
+      </figure>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- Tables -->
+<xsl:template match="table" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <table>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </table>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="table-to-v2"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="table-to-v2">
+  <texttable>
+    <xsl:apply-templates select="@anchor|@align" mode="cleanup"/>
+    <xsl:if test="not(@align)">
+      <xsl:attribute name="align">left</xsl:attribute>
+    </xsl:if>
+    <xsl:variable name="title">
+      <xsl:choose>
+        <xsl:when test="name">
+          <xsl:variable name="hold">
+            <xsl:apply-templates select="name/node()"/>
+          </xsl:variable>
+          <xsl:value-of select="normalize-space($hold)"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@title"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$title!=''">
+      <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+    </xsl:if>
+    <xsl:if test="count(thead/tr) > 1">
+      <xsl:call-template name="error">
+        <xsl:with-param name="inline">no</xsl:with-param>
+        <xsl:with-param name="msg">Multiple table header lines not supported</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:for-each select="thead/tr/*">
+      <xsl:variable name="p" select="position()"/>
+      <!-- in texttable the whole column has the same alignment; we try
+      either the first non-header row or the header itself-->
+      <xsl:variable name="align">
+        <xsl:choose>
+          <xsl:when test="tbody/tr[1]/*[1] and tbody/tr[1]/*[1]/@align"><xsl:value-of select="tbody/tr[1]/*[1]/@align"/></xsl:when>
+          <xsl:when test="@align"><xsl:value-of select="@align"/></xsl:when>
+          <xsl:otherwise>left</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <ttcol align="{$align}">
+        <xsl:apply-templates mode="cleanup"/>
+      </ttcol>
+    </xsl:for-each>
+    <xsl:for-each select="tbody/tr/*">
+      <c>
+        <xsl:if test="position()=1">
+          <xsl:apply-templates select="../../../iref" mode="cleanup"/>
+        </xsl:if>
+        <xsl:choose>
+          <xsl:when test="t|sourcecode|ol|dl|uo">
+            <xsl:apply-templates select="t/node()|sourcecode/node()|ol/li/node()|ul/li/node()|dl/*/node()" mode="cleanup"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates mode="cleanup"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </c>
+      <xsl:if test="@rowspan and @rowspan!='1'">
+        <xsl:call-template name="error">
+          <xsl:with-param name="inline">no</xsl:with-param>
+          <xsl:with-param name="msg">rowspan attribute not supported (dropped, table will be ugly)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:if test="@colspan and @colspan!='1'">
+        <xsl:call-template name="error">
+          <xsl:with-param name="inline">no</xsl:with-param>
+          <xsl:with-param name="msg">colspan attribute not supported (dropped, table will be ugly)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:for-each>
+    <xsl:if test="tfoot">
+      <xsl:call-template name="error">
+        <xsl:with-param name="inline">no</xsl:with-param>
+        <xsl:with-param name="msg">tfoot element not supported (dropped)</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </texttable>
+</xsl:template>
+
+<!-- date formats -->
+<xsl:template match="/rfc/front/date/@month" mode="cleanup">
+  <xsl:attribute name="month">
+    <xsl:choose>
+      <xsl:when test="string(number(.))!='NaN' and number(.)&gt;0 and number(.)&lt;13">
+        <xsl:call-template name="get-month-as-name">
+          <xsl:with-param name="month" select="."/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="."/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:attribute>
+</xsl:template>
+
+<!-- x:contributor/contact -->
+<xsl:template match="x:contributor|contact" mode="cleanup">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-xml2rfc-voc >= 3">
+      <contact>
+        <xsl:apply-templates select="@*|node()" mode="cleanup"/>
+      </contact>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="content">
+        <xsl:apply-templates select="."/>
+      </xsl:variable>
+      <t>
+        <xsl:apply-templates select="exslt:node-set($content)/*" mode="text"/>
+      </t>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="*" mode="text">
+  <xsl:apply-templates mode="text"/>
+</xsl:template>
+<xsl:template match="text()" mode="text">
+  <xsl:value-of select="."/>
+</xsl:template>
+<xsl:template match="br" mode="text">
+  <vspace blankLines="0"/>
+</xsl:template>
+
+<!-- x:include -->
+<xsl:template match="/rfc/back/references/xi:include|/rfc/back/references/referencegroup/xi:include" mode="cleanup">
+  <xsl:apply-templates select="document(@href)" mode="cleanup"/>
+</xsl:template>
+
+<!-- Display names for references -->
+<xsl:template match="displayreference" mode="cleanup"/>
+<xsl:template match="reference/@anchor[.=/rfc/back/displayreference/@target]" mode="cleanup">
+  <xsl:attribute name="anchor">
+    <xsl:call-template name="generate-ref-name"/>
+  </xsl:attribute>
+</xsl:template>
+<xsl:template match="xref/@target[.=/rfc/back/displayreference/@target]" mode="cleanup">
+  <xsl:attribute name="target">
+    <xsl:call-template name="generate-ref-name"/>
+  </xsl:attribute>
+</xsl:template>
+
+<xsl:template name="generate-ref-name">
+  <xsl:variable name="tnewname">
+    <xsl:value-of select="/rfc/back/displayreference[@target=current()]/@to"/>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="count(/rfc/back/displayreference[@to=current()])>1 or //reference[@anchor=$tnewname]">
+      <xsl:value-of select="current()"/>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Not rewriting reference name <xsl:value-of select="current()"/> as it would conflict</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="translate(substring($tnewname,1,1),$digits,'')=''">
+      <xsl:value-of select="concat('_',$tnewname)"/>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">rewriting reference name '<xsl:value-of select="$tnewname"/>' to '<xsl:value-of select="concat('_',$tnewname)"/>' due to illegal start character</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$tnewname"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+</xsl:transform>
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/lib/cssmap.xml b/test/fixtures/cache-tests/spec/lib/cssmap.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/cssmap.xml
@@ -0,0 +1,15 @@
+<css>
+  <map from="error" css="bg-danger"/>
+  <map from="fbbutton" css="btn btn-primary fbbutton"/>
+  <map from="feedback" css="btn btn-primary feedback"/>
+  <map from="header" css="table table-condensed header"/>
+  <map from="docstatus" css="alert alert-info"/>
+  <map from="noprint" css="hidden-print"/>
+  <map from="note" css="alert alert-warning"/>
+  <map from="tcenter" css="text-center"/>
+  <map from="left" css="text-left"/>
+  <map from="right" css="text-right"/>
+  <map from="reference" css="dl-horizontal"/>
+  <map from="tt" css="table table-condensed table-striped"/>
+  <map from="publishedasrfc" css="alert alert-danger"/>
+</css>
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.2119.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.2119.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.2119.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC2119' target='http://www.rfc-editor.org/info/rfc2119'>
+<front>
+<title>Key words for use in RFCs to Indicate Requirement Levels</title>
+<author initials='S.' surname='Bradner' fullname='S. Bradner'><organization /></author>
+<date year='1997' month='March' />
+<abstract><t>In many standards track documents several words are used to signify the requirements in the specification.  These words are often capitalized. This document defines these words as they should be interpreted in IETF documents.  This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t></abstract>
+</front>
+<seriesInfo name='BCP' value='14'/>
+<seriesInfo name='RFC' value='2119'/>
+<seriesInfo name='DOI' value='10.17487/RFC2119'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.2818.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.2818.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.2818.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC2818' target='http://www.rfc-editor.org/info/rfc2818'>
+<front>
+<title>HTTP Over TLS</title>
+<author initials='E.' surname='Rescorla' fullname='E. Rescorla'><organization /></author>
+<date year='2000' month='May' />
+<abstract><t>This memo describes how to use Transport Layer Security (TLS) to secure Hypertext Transfer Protocol (HTTP) connections over the Internet.  This memo provides information for the Internet community.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='2818'/>
+<seriesInfo name='DOI' value='10.17487/RFC2818'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.3864.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.3864.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.3864.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC3864' target='https://www.rfc-editor.org/info/rfc3864'>
+<front>
+<title>Registration Procedures for Message Header Fields</title>
+<author initials='G.' surname='Klyne' fullname='G. Klyne'><organization /></author>
+<author initials='M.' surname='Nottingham' fullname='M. Nottingham'><organization /></author>
+<author initials='J.' surname='Mogul' fullname='J. Mogul'><organization /></author>
+<date year='2004' month='September' />
+<abstract><t>This specification defines registration procedures for the message header fields used by Internet mail, HTTP, Netnews and other applications.  This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t></abstract>
+</front>
+<seriesInfo name='BCP' value='90'/>
+<seriesInfo name='RFC' value='3864'/>
+<seriesInfo name='DOI' value='10.17487/RFC3864'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5023.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5023.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5023.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC5023' target='https://www.rfc-editor.org/info/rfc5023'>
+<front>
+<title>The Atom Publishing Protocol</title>
+<author initials='J.' surname='Gregorio' fullname='J. Gregorio' role='editor'><organization /></author>
+<author initials='B.' surname='de hOra' fullname='B. de hOra' role='editor'><organization /></author>
+<date year='2007' month='October' />
+<abstract><t>The Atom Publishing Protocol (AtomPub) is an application-level protocol for publishing and editing Web resources.  The protocol is based on HTTP transfer of Atom-formatted representations.  The Atom format is documented in the Atom Syndication Format.  [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='RFC' value='5023'/>
+<seriesInfo name='DOI' value='10.17487/RFC5023'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5226.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5226.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5226.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC5226' target='https://www.rfc-editor.org/info/rfc5226'>
+<front>
+<title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
+<author initials='T.' surname='Narten' fullname='T. Narten'><organization /></author>
+<author initials='H.' surname='Alvestrand' fullname='H. Alvestrand'><organization /></author>
+<date year='2008' month='May' />
+<abstract><t>Many protocols make use of identifiers consisting of constants and other well-known values.  Even after a protocol has been defined and deployment has begun, new values may need to be assigned (e.g., for a new option type in DHCP, or a new encryption or authentication transform for IPsec).  To ensure that such quantities have consistent values and interpretations across all implementations, their assignment must be administered by a central authority.  For IETF protocols, that role is provided by the Internet Assigned Numbers Authority (IANA).</t><t>In order for IANA to manage a given namespace prudently, it needs guidelines describing the conditions under which new values can be assigned or when modifications to existing values can be made.  If IANA is expected to play a role in the management of a namespace, IANA must be given clear and concise instructions describing that role.  This document discusses issues that should be considered in formulating a policy for assigning values to a namespace and provides guidelines for authors on the specific text that must be included in documents that place demands on IANA.</t><t>This document obsoletes RFC 2434.  This document specifies an Internet Best  Current Practices for the Internet Community, and requests discussion and  suggestions for improvements.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='5226'/>
+<seriesInfo name='DOI' value='10.17487/RFC5226'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5234.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5234.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5234.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC5234' target='https://www.rfc-editor.org/info/rfc5234'>
+<front>
+<title>Augmented BNF for Syntax Specifications: ABNF</title>
+<author initials='D.' surname='Crocker' fullname='D. Crocker' role='editor'><organization /></author>
+<author initials='P.' surname='Overell' fullname='P. Overell'><organization /></author>
+<date year='2008' month='January' />
+<abstract><t>Internet technical specifications often need to define a formal syntax.  Over the years, a modified version of Backus-Naur Form (BNF), called Augmented BNF (ABNF), has been popular among many Internet specifications.  The current specification documents ABNF. It balances compactness and simplicity with reasonable representational power.  The differences between standard BNF and ABNF involve naming rules, repetition, alternatives, order-independence, and value ranges.  This specification also supplies additional rule definitions and encoding for a core lexical analyzer of the type common to several Internet specifications.  [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='STD' value='68'/>
+<seriesInfo name='RFC' value='5234'/>
+<seriesInfo name='DOI' value='10.17487/RFC5234'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5246.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5246.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5246.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC5246' target='http://www.rfc-editor.org/info/rfc5246'>
+<front>
+<title>The Transport Layer Security (TLS) Protocol Version 1.2</title>
+<author initials='T.' surname='Dierks' fullname='T. Dierks'><organization /></author>
+<author initials='E.' surname='Rescorla' fullname='E. Rescorla'><organization /></author>
+<date year='2008' month='August' />
+<abstract><t>This document specifies Version 1.2 of the Transport Layer Security (TLS) protocol.  The TLS protocol provides communications security over the Internet.  The protocol allows client/server applications to communicate in a way that is designed to prevent eavesdropping, tampering, or message forgery.  [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='RFC' value='5246'/>
+<seriesInfo name='DOI' value='10.17487/RFC5246'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5280.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5280.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5280.xml
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC5280' target='https://www.rfc-editor.org/info/rfc5280'>
+<front>
+<title>Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile</title>
+<author initials='D.' surname='Cooper' fullname='D. Cooper'><organization /></author>
+<author initials='S.' surname='Santesson' fullname='S. Santesson'><organization /></author>
+<author initials='S.' surname='Farrell' fullname='S. Farrell'><organization /></author>
+<author initials='S.' surname='Boeyen' fullname='S. Boeyen'><organization /></author>
+<author initials='R.' surname='Housley' fullname='R. Housley'><organization /></author>
+<author initials='W.' surname='Polk' fullname='W. Polk'><organization /></author>
+<date year='2008' month='May' />
+<abstract><t>This memo profiles the X.509 v3 certificate and X.509 v2 certificate revocation list (CRL) for use in the Internet.  An overview of this approach and model is provided as an introduction.  The X.509 v3 certificate format is described in detail, with additional information regarding the format and semantics of Internet name forms.  Standard certificate extensions are described and two Internet-specific extensions are defined.  A set of required certificate extensions is specified.  The X.509 v2 CRL format is described in detail along with standard and Internet-specific extensions.  An algorithm for X.509 certification path validation is described.  An ASN.1 module and examples are provided in the appendices.  [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='RFC' value='5280'/>
+<seriesInfo name='DOI' value='10.17487/RFC5280'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5785.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5785.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.5785.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC5785' target='http://www.rfc-editor.org/info/rfc5785'>
+<front>
+<title>Defining Well-Known Uniform Resource Identifiers (URIs)</title>
+<author initials='M.' surname='Nottingham' fullname='M. Nottingham'><organization /></author>
+<author initials='E.' surname='Hammer-Lahav' fullname='E. Hammer-Lahav'><organization /></author>
+<date year='2010' month='April' />
+<abstract><t>This memo defines a path prefix for &quot;well-known locations&quot;, &quot;/.well-known/&quot;, in selected Uniform Resource Identifier (URI) schemes.   [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='RFC' value='5785'/>
+<seriesInfo name='DOI' value='10.17487/RFC5785'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6066.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6066.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6066.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC6066' target='https://www.rfc-editor.org/info/rfc6066'>
+<front>
+<title>Transport Layer Security (TLS) Extensions: Extension Definitions</title>
+<author initials='D.' surname='Eastlake 3rd' fullname='D. Eastlake 3rd'><organization /></author>
+<date year='2011' month='January' />
+<abstract><t>This document provides specifications for existing TLS extensions.  It is a companion document for RFC 5246, &quot;The Transport Layer Security (TLS) Protocol Version 1.2&quot;.  The extensions specified are server_name, max_fragment_length, client_certificate_url, trusted_ca_keys, truncated_hmac, and status_request.  [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='RFC' value='6066'/>
+<seriesInfo name='DOI' value='10.17487/RFC6066'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6454.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6454.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6454.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC6454' target='http://www.rfc-editor.org/info/rfc6454'>
+<front>
+<title>The Web Origin Concept</title>
+<author initials='A.' surname='Barth' fullname='A. Barth'><organization /></author>
+<date year='2011' month='December' />
+<abstract><t>This document defines the concept of an &quot;origin&quot;, which is often used as the scope of authority or privilege by user agents.  Typically, user agents isolate content retrieved from different origins to prevent malicious web site operators from interfering with the operation of benign web sites.  In addition to outlining the principles that underlie the concept of origin, this document details how to determine the origin of a URI and how to serialize an origin into a string.  It also defines an HTTP header field, named &quot;Origin&quot;, that indicates which origins are associated with an HTTP request.   [STANDARDS-TRACK]</t></abstract>
+</front>
+<seriesInfo name='RFC' value='6454'/>
+<seriesInfo name='DOI' value='10.17487/RFC6454'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6960.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6960.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6960.xml
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC6960' target='https://www.rfc-editor.org/info/rfc6960'>
+<front>
+<title>X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP</title>
+<author initials='S.' surname='Santesson' fullname='S. Santesson'><organization /></author>
+<author initials='M.' surname='Myers' fullname='M. Myers'><organization /></author>
+<author initials='R.' surname='Ankney' fullname='R. Ankney'><organization /></author>
+<author initials='A.' surname='Malpani' fullname='A. Malpani'><organization /></author>
+<author initials='S.' surname='Galperin' fullname='S. Galperin'><organization /></author>
+<author initials='C.' surname='Adams' fullname='C. Adams'><organization /></author>
+<date year='2013' month='June' />
+<abstract><t>This document specifies a protocol useful in determining the current status of a digital certificate without requiring Certificate Revocation Lists (CRLs). Additional mechanisms addressing PKIX operational requirements are specified in separate documents.  This document obsoletes RFCs 2560 and 6277.  It also updates RFC 5912.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='6960'/>
+<seriesInfo name='DOI' value='10.17487/RFC6960'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6962.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6962.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.6962.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC6962' target='https://www.rfc-editor.org/info/rfc6962'>
+<front>
+<title>Certificate Transparency</title>
+<author initials='B.' surname='Laurie' fullname='B. Laurie'><organization /></author>
+<author initials='A.' surname='Langley' fullname='A. Langley'><organization /></author>
+<author initials='E.' surname='Kasper' fullname='E. Kasper'><organization /></author>
+<date year='2013' month='June' />
+<abstract><t>This document describes an experimental protocol for publicly logging the existence of Transport Layer Security (TLS) certificates as they are issued or observed, in a manner that allows anyone to audit certificate authority (CA) activity and notice the issuance of suspect certificates as well as to audit the certificate logs themselves.  The intent is that eventually clients would refuse to honor certificates that do not appear in a log, effectively forcing CAs to add all issued certificates to the logs.</t><t>Logs are network services that implement the protocol operations for submissions and queries that are defined in this document.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='6962'/>
+<seriesInfo name='DOI' value='10.17487/RFC6962'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7159.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7159.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7159.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7159' target='http://www.rfc-editor.org/info/rfc7159'>
+<front>
+<title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
+<author initials='T.' surname='Bray' fullname='T. Bray' role='editor'><organization /></author>
+<date year='2014' month='March' />
+<abstract><t>JavaScript Object Notation (JSON) is a lightweight, text-based, language-independent data interchange format.  It was derived from the ECMAScript Programming Language Standard.  JSON defines a small set of formatting rules for the portable representation of structured data.</t><t>This document removes inconsistencies with other specifications of JSON, repairs specification errors, and offers experience-based interoperability guidance.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7159'/>
+<seriesInfo name='DOI' value='10.17487/RFC7159'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7230.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7230.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7230.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7230' target='http://www.rfc-editor.org/info/rfc7230'>
+<front>
+<title>Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing</title>
+<author initials='R.' surname='Fielding' fullname='R. Fielding' role='editor'><organization /></author>
+<author initials='J.' surname='Reschke' fullname='J. Reschke' role='editor'><organization /></author>
+<date year='2014' month='June' />
+<abstract><t>The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems.  This document provides an overview of HTTP architecture and its associated terminology, defines the &quot;http&quot; and &quot;https&quot; Uniform Resource Identifier (URI) schemes, defines the HTTP/1.1 message syntax and parsing requirements, and describes related security concerns for implementations.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7230'/>
+<seriesInfo name='DOI' value='10.17487/RFC7230'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7232.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7232.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7232.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7232' target='http://www.rfc-editor.org/info/rfc7232'>
+<front>
+<title>Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests</title>
+<author initials='R.' surname='Fielding' fullname='R. Fielding' role='editor'><organization /></author>
+<author initials='J.' surname='Reschke' fullname='J. Reschke' role='editor'><organization /></author>
+<date year='2014' month='June' />
+<abstract><t>The Hypertext Transfer Protocol (HTTP) is a stateless application- level protocol for distributed, collaborative, hypertext information systems.  This document defines HTTP/1.1 conditional requests, including metadata header fields for indicating state changes, request header fields for making preconditions on such state, and rules for constructing the responses to a conditional request when one or more preconditions evaluate to false.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7232'/>
+<seriesInfo name='DOI' value='10.17487/RFC7232'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7234.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7234.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7234.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7234' target='http://www.rfc-editor.org/info/rfc7234'>
+<front>
+<title>Hypertext Transfer Protocol (HTTP/1.1): Caching</title>
+<author initials='R.' surname='Fielding' fullname='R. Fielding' role='editor'><organization /></author>
+<author initials='M.' surname='Nottingham' fullname='M. Nottingham' role='editor'><organization /></author>
+<author initials='J.' surname='Reschke' fullname='J. Reschke' role='editor'><organization /></author>
+<date year='2014' month='June' />
+<abstract><t>The Hypertext Transfer Protocol (HTTP) is a stateless \%application- level protocol for distributed, collaborative, hypertext information systems.  This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7234'/>
+<seriesInfo name='DOI' value='10.17487/RFC7234'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7258.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7258.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7258.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7258' target='http://www.rfc-editor.org/info/rfc7258'>
+<front>
+<title>Pervasive Monitoring Is an Attack</title>
+<author initials='S.' surname='Farrell' fullname='S. Farrell'><organization /></author>
+<author initials='H.' surname='Tschofenig' fullname='H. Tschofenig'><organization /></author>
+<date year='2014' month='May' />
+<abstract><t>Pervasive monitoring is a technical attack that should be mitigated in the design of IETF protocols, where possible.</t></abstract>
+</front>
+<seriesInfo name='BCP' value='188'/>
+<seriesInfo name='RFC' value='7258'/>
+<seriesInfo name='DOI' value='10.17487/RFC7258'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7435.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7435.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7435.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7435' target='http://www.rfc-editor.org/info/rfc7435'>
+<front>
+<title>Opportunistic Security: Some Protection Most of the Time</title>
+<author initials='V.' surname='Dukhovni' fullname='V. Dukhovni'><organization /></author>
+<date year='2014' month='December' />
+<abstract><t>This document defines the concept &quot;Opportunistic Security&quot; in the context of communications protocols.  Protocol designs based on Opportunistic Security use encryption even when authentication is not available, and use authentication when possible, thereby removing barriers to the widespread use of encryption on the Internet.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7435'/>
+<seriesInfo name='DOI' value='10.17487/RFC7435'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7540.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7540.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7540.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7540' target='http://www.rfc-editor.org/info/rfc7540'>
+<front>
+<title>Hypertext Transfer Protocol Version 2 (HTTP/2)</title>
+<author initials='M.' surname='Belshe' fullname='M. Belshe'><organization /></author>
+<author initials='R.' surname='Peon' fullname='R. Peon'><organization /></author>
+<author initials='M.' surname='Thomson' fullname='M. Thomson' role='editor'><organization /></author>
+<date year='2015' month='May' />
+<abstract><t>This specification describes an optimized expression of the semantics of the Hypertext Transfer Protocol (HTTP), referred to as HTTP version 2 (HTTP/2).  HTTP/2 enables a more efficient use of network resources and a reduced perception of latency by introducing header field compression and allowing multiple concurrent exchanges on the same connection.  It also introduces unsolicited push of representations from servers to clients.</t><t>This specification is an alternative to, but does not obsolete, the HTTP/1.1 message syntax.  HTTP's existing semantics remain unchanged.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7540'/>
+<seriesInfo name='DOI' value='10.17487/RFC7540'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7838.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7838.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.7838.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC7838' target='http://www.rfc-editor.org/info/rfc7838'>
+<front>
+<title>HTTP Alternative Services</title>
+<author initials='M.' surname='Nottingham' fullname='M. Nottingham'><organization /></author>
+<author initials='P.' surname='McManus' fullname='P. McManus'><organization /></author>
+<author initials='J.' surname='Reschke' fullname='J. Reschke'><organization /></author>
+<date year='2016' month='April' />
+<abstract><t>This document specifies &quot;Alternative Services&quot; for HTTP, which allow an origin's resources to be authoritatively available at a separate network location, possibly accessed with a different protocol configuration.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='7838'/>
+<seriesInfo name='DOI' value='10.17487/RFC7838'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.8174.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.8174.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.8174.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC8174' target='https://www.rfc-editor.org/info/rfc8174'>
+<front>
+<title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
+<author initials='B.' surname='Leiba' fullname='B. Leiba'><organization /></author>
+<date year='2017' month='May' />
+<abstract><t>RFC 2119 specifies common key words that may be used in protocol  specifications.  This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the  defined special meanings.</t></abstract>
+</front>
+<seriesInfo name='BCP' value='14'/>
+<seriesInfo name='RFC' value='8174'/>
+<seriesInfo name='DOI' value='10.17487/RFC8174'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.8288.xml b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.8288.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/ref/reference.RFC.8288.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<reference  anchor='RFC8288' target='https://www.rfc-editor.org/info/rfc8288'>
+<front>
+<title>Web Linking</title>
+<author initials='M.' surname='Nottingham' fullname='M. Nottingham'><organization /></author>
+<date year='2017' month='October' />
+<abstract><t>This specification defines a model for the relationships between resources on the Web (&quot;links&quot;) and the type of those relationships (&quot;link relation types&quot;).</t><t>It also defines the serialisation of such links in HTTP headers with the Link header field.</t></abstract>
+</front>
+<seriesInfo name='RFC' value='8288'/>
+<seriesInfo name='DOI' value='10.17487/RFC8288'/>
+</reference>
diff --git a/test/fixtures/cache-tests/spec/lib/rfc2629-no-doctype.xslt b/test/fixtures/cache-tests/spec/lib/rfc2629-no-doctype.xslt
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/rfc2629-no-doctype.xslt
@@ -0,0 +1,13505 @@
+<!--
+    XSLT transformation from RFC2629 XML format to HTML
+
+    Copyright (c) 2006-2020, Julian Reschke (julian.reschke@greenbytes.de)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Julian Reschke nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="2.0"
+
+                xmlns:date="http://exslt.org/dates-and-times"
+                xmlns:ed="http://greenbytes.de/2002/rfcedit"
+                xmlns:exslt="http://exslt.org/common"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
+                xmlns:myns="mailto:julian.reschke@greenbytes.de?subject=rfc2629.xslt"
+                xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+                xmlns:saxon="http://saxon.sf.net/"
+                xmlns:saxon-old="http://icl.com/saxon"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:x="http://purl.org/net/xml2rfc/ext"
+                xmlns:xi="http://www.w3.org/2001/XInclude"
+                xmlns:xhtml="http://www.w3.org/1999/xhtml"
+
+                exclude-result-prefixes="date ed exslt fo msxsl myns rdf saxon saxon-old svg x xi xhtml"
+                >
+
+<xsl:strip-space elements="abstract address artset aside author back boilerplate dl figure front list middle note ol postal reference references rfc section table tbody thead tr texttable ul svg:svg"/>
+
+
+<!-- PIs outside the root element, or inside the root element but before <front> -->
+<xsl:variable name="global-std-pis" select="/processing-instruction('rfc') | /*/processing-instruction('rfc')[following-sibling::front]"/>
+
+<!-- rfc authorship PI -->
+
+<xsl:param name="xml2rfc-authorship">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'authorship'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc comments PI -->
+
+<xsl:param name="xml2rfc-comments">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'comments'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc compact PI -->
+
+<xsl:param name="xml2rfc-compact">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'compact'"/>
+    <xsl:with-param name="default" select="$xml2rfc-rfcedstyle"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc footer PI -->
+
+<xsl:param name="xml2rfc-footer">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'footer'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc header PI -->
+
+<xsl:param name="xml2rfc-header">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'header'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc inline PI -->
+
+<xsl:param name="xml2rfc-inline">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'inline'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- include a table of contents if a processing instruction <?rfc?>
+     exists with contents toc="yes". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-toc">
+  <xsl:variable name="default">
+    <xsl:choose>
+      <xsl:when test="/rfc/@version >= 3">yes</xsl:when>
+      <xsl:otherwise>no</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="/rfc/@tocInclude='false'">no</xsl:when>
+    <xsl:when test="/rfc/@tocInclude='true'">yes</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'toc'"/>
+        <xsl:with-param name="default" select="$default"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- optional tocdepth-->
+
+<xsl:param name="xml2rfc-tocdepth">
+  <xsl:choose>
+    <xsl:when test="/rfc/@tocDepth">
+      <xsl:value-of select="/rfc/@tocDepth"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'tocdepth'"/>
+        <xsl:with-param name="default" select="'3'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:variable name="parsedTocDepth">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-tocdepth='1'">1</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='2'">2</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='3'">3</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='4'">4</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='5'">5</xsl:when>
+    <xsl:otherwise>99</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- suppress top block if a processing instruction <?rfc?>
+     exists with contents tocblock="no". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-topblock">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'topblock'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- Format to the RFC Editor's taste -->
+
+<xsl:param name="xml2rfc-rfcedstyle">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'rfcedstyle'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- the name of an automatically inserted references section -->
+
+<xsl:param name="xml2rfc-refparent">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'refparent'"/>
+    <xsl:with-param name="default" select="'References'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- use symbolic reference names instead of numeric ones unless a processing instruction <?rfc?>
+     exists with contents symrefs="no". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-symrefs">
+  <xsl:choose>
+    <xsl:when test="/rfc/@symRefs='false'">no</xsl:when>
+    <xsl:when test="/rfc/@symRefs='true'">yes</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'symrefs'"/>
+        <xsl:with-param name="default" select="'yes'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- sort references if a processing instruction <?rfc?>
+     exists with contents sortrefs="yes". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-sortrefs">
+  <xsl:choose>
+    <xsl:when test="/rfc/@sortRefs='true'">yes</xsl:when>
+    <xsl:when test="/rfc/@sortRefs='false'">no</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'sortrefs'"/>
+        <xsl:with-param name="default" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- insert editing marks if a processing instruction <?rfc?>
+     exists with contents editing="yes". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-editing">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'editing'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- make it a private paper -->
+
+<xsl:param name="xml2rfc-private">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'private'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- background image? -->
+
+<xsl:param name="xml2rfc-background">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'background'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- override CSS? -->
+
+<xsl:param name="xml2rfc-ext-css-resource">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'css-resource'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-css-contents">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'css-contents'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- CSS max page width -->
+
+<xsl:param name="xml2rfc-ext-maxwidth">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'maxwidth'"/>
+    <xsl:with-param name="default" select="'1000'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:variable name="parsedMaxwidth">
+  <xsl:choose>
+    <xsl:when test="string(number($xml2rfc-ext-maxwidth)) != 'NaN'">
+      <xsl:value-of select="$xml2rfc-ext-maxwidth"/>
+    </xsl:when>
+    <xsl:when test="$xml2rfc-ext-maxwidth='none'"></xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('Unsupported value of rfc-ext maxwidth PI: ', $xml2rfc-ext-maxwidth)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- CSS styles -->
+
+<xsl:param name="xml2rfc-ext-styles">fft-sans-serif ffb-serif ff-cleartype</xsl:param>
+
+<xsl:variable name="styles" select="concat(' ',normalize-space($xml2rfc-ext-styles),' ')"/>
+
+<xsl:param name="xml2rfc-ext-ff-body">
+  <xsl:variable name="t">
+    <xsl:if test="contains($styles,' ff-noto ')">
+      <xsl:if test="contains($styles,' ffb-serif ')">
+        'Noto Serif', 
+      </xsl:if>
+      <xsl:if test="contains($styles,' ffb-sans-serif ')">
+        'Noto Sans',
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ff-cleartype ')">
+      <xsl:if test="contains($styles,' ffb-serif ')">
+        cambria, georgia, 
+      </xsl:if>
+      <xsl:if test="contains($styles,' ffb-sans-serif ')">
+        candara, calibri,
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ffb-sans-serif ')">
+      segoe, optima, arial, sans-serif,
+    </xsl:if>
+    serif
+  </xsl:variable>
+  <xsl:call-template name="ff-list">
+    <xsl:with-param name="s" select="normalize-space($t)"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-ff-title">
+  <xsl:variable name="t">
+    <xsl:if test="contains($styles,' ff-noto ')">
+      <xsl:if test="contains($styles,' fft-serif ')">
+        'Noto Serif', 
+      </xsl:if>
+      <xsl:if test="contains($styles,' fft-sans-serif ')">
+        'Noto Sans',
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ff-cleartype ')">
+      <xsl:if test="contains($styles,' fft-serif ')">
+        cambria, georgia, 
+      </xsl:if>
+      <xsl:if test="contains($styles,' fft-sans-serif ')">
+        candara, calibri,
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' fft-serif ')">
+      serif,
+    </xsl:if>
+    <xsl:if test="contains($styles,' fft-sans-serif ')">
+      segoe, optima, arial,
+    </xsl:if>
+    sans-serif
+  </xsl:variable>
+  <xsl:call-template name="ff-list">
+    <xsl:with-param name="s" select="normalize-space($t)"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-ff-pre">
+  <xsl:variable name="t">
+    <xsl:if test="contains($styles,' ff-noto ')">
+      'Roboto Mono',
+    </xsl:if>
+    <xsl:if test="contains($styles,' ff-cleartype ')">
+      consolas, monaco,
+    </xsl:if>
+    monospace
+  </xsl:variable>
+  <xsl:call-template name="ff-list">
+    <xsl:with-param name="s" select="normalize-space($t)"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-webfonts">
+  <xsl:if test="contains($styles,' ff-noto ')">
+    <xsl:if test="contains($styles,' ffb-sans-serif ') or contains($styles,' fft-sans-serif ')">
+      <xsl:text>@import url('https://fonts.googleapis.com/css?family=Noto+Sans:r,b,i,bi');&#10;</xsl:text>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ffb-serif ') or contains($styles,' fft-serif ')">
+      <xsl:text>@import url('https://fonts.googleapis.com/css?family=Noto+Serif:r,b,i,bi');&#10;</xsl:text>
+    </xsl:if>
+    <xsl:text>@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:r,b,i,bi');&#10;</xsl:text>
+  </xsl:if>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-dark-mode">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'dark-mode'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="ff-list">
+  <xsl:param name="s"/>
+  <xsl:choose>
+    <xsl:when test="not(contains($s,','))">
+      <xsl:value-of select="normalize-space($s)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="normalize-space(substring-before($s,','))"/>
+      <xsl:text>, </xsl:text>
+      <xsl:call-template name="ff-list">
+        <xsl:with-param name="s" select="substring-after($s,',')"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- include PI -->
+
+<xsl:template name="getIncludes">
+  <xsl:param name="nodes"/>
+  <xsl:for-each select="$nodes">
+    <xsl:variable name="uri1">
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="."/>
+        <xsl:with-param name="attr" select="'include'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:if test="$uri1!=''">
+      <xsl:variable name="tbase" select="substring-before($uri1, '?')"/>
+      <xsl:variable name="base"><xsl:choose><xsl:when test="$tbase!=''"><xsl:value-of select="$tbase"/></xsl:when><xsl:otherwise><xsl:value-of select="$uri1"/></xsl:otherwise></xsl:choose></xsl:variable>
+      <xsl:variable name="tquery" select="substring-after($uri1, '?')"/>
+      <xsl:variable name="query"><xsl:if test="$tquery!=''">?</xsl:if><xsl:value-of select="$tquery"/></xsl:variable>
+      <xsl:variable name="ends-with-xml" select="substring($base, string-length($base)-3)='.xml'"/>
+      <xsl:variable name="for-draft" select="contains($base,'reference.I-D')"/>
+      <xsl:variable name="uri2" select="concat($base,'.xml',$query)"/>
+      <xsl:variable name="uri3r" select="concat($toolsBaseUriForRFCReferences,$base,$query)"/>
+      <xsl:variable name="uri4r" select="concat($toolsBaseUriForRFCReferences,$base,'.xml',$query)"/>
+      <xsl:variable name="uri3i" select="concat($toolsBaseUriForIDReferences,$base,$query)"/>
+      <xsl:variable name="uri4i" select="concat($toolsBaseUriForIDReferences,$base,'.xml',$query)"/>
+      <xsl:choose>
+        <xsl:when test="not($ends-with-xml) and document($uri2)/reference">
+          <xsl:call-template name="include-uri-warning">
+            <xsl:with-param name="specified" select="$uri1"/>
+            <xsl:with-param name="success" select="$uri2"/>
+          </xsl:call-template>
+          <myns:include from="{$uri2}" in="{generate-id(..)}">
+            <xsl:copy-of select="document($uri2)"/>
+          </myns:include>
+        </xsl:when>
+        <xsl:when test="document($uri1)/reference">
+          <myns:include from="{$uri1}" in="{generate-id(..)}">
+            <xsl:copy-of select="document($uri1)"/>
+          </myns:include>
+        </xsl:when>
+        <xsl:when test="not($ends-with-xml) and $for-draft and not(contains($uri1,':')) and document($uri4i)/reference">
+          <xsl:call-template name="include-uri-warning">
+            <xsl:with-param name="specified" select="$uri1"/>
+            <xsl:with-param name="success" select="$uri4i"/>
+          </xsl:call-template>
+          <myns:include from="{$uri4i}" in="{generate-id(..)}">
+            <xsl:copy-of select="document($uri4i)"/>
+          </myns:include>
+        </xsl:when>
+        <xsl:when test="not(contains($uri1,':')) and $for-draft and document($uri3i)/reference">
+          <xsl:call-template name="include-uri-warning">
+            <xsl:with-param name="specified" select="$uri1"/>
+            <xsl:with-param name="success" select="$uri3i"/>
+          </xsl:call-template>
+          <myns:include from="{$uri3i}" in="{generate-id(..)}">
+            <xsl:copy-of select="document($uri3i)"/>
+          </myns:include>
+        </xsl:when>
+        <xsl:when test="not($ends-with-xml) and not(contains($uri1,':')) and document($uri4r)/reference">
+          <xsl:call-template name="include-uri-warning">
+            <xsl:with-param name="specified" select="$uri1"/>
+            <xsl:with-param name="success" select="$uri4r"/>
+          </xsl:call-template>
+          <myns:include from="{$uri4r}" in="{generate-id(..)}">
+            <xsl:copy-of select="document($uri4r)"/>
+          </myns:include>
+        </xsl:when>
+        <xsl:when test="not(contains($uri1,':')) and document($uri3r)/reference">
+          <xsl:call-template name="include-uri-warning">
+            <xsl:with-param name="specified" select="$uri1"/>
+            <xsl:with-param name="success" select="$uri3r"/>
+          </xsl:call-template>
+          <myns:include from="{$uri3r}" in="{generate-id(..)}">
+            <xsl:copy-of select="document($uri3r)"/>
+          </myns:include>
+        </xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="include-uri-warning">
+  <xsl:param name="specified"/>
+  <xsl:param name="success"/>
+  <xsl:call-template name="warning">
+    <xsl:with-param name="msg">include succeeded for best-guess URI <xsl:value-of select="$success"/> while <xsl:value-of select="$specified"/> was specified - you may want to adjust the include directive in order to avoid future warnings</xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="getXIncludes">
+  <xsl:param name="nodes"/>
+  <xsl:for-each select="$nodes">
+    <xsl:choose>
+      <xsl:when test="(@parse and @parse!='xml') or @xpointer">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="'Unsupported attributes on x:include element'"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="uri">
+          <!--<xsl:choose>
+            <xsl:when test="starts-with(@href,'https://xml2rfc.ietf.org/public/rfc/')">
+              <xsl:call-template name="warning">
+                <xsl:with-param name="msg">rewriting URI to /xml2rfc.tools.ietf.org for <xsl:value-of select="@href"/> - see in order to avoid broken server's 403 response (see https://mailarchive.ietf.org/arch/msg/xml2rfc/56sDqFVKF0baqdgEjHQtxOUMf4o).</xsl:with-param>
+              </xsl:call-template>
+              <xsl:value-of select="concat('https://xml2rfc.tools.ietf.org/public/rfc/',substring-after(@href,'https://xml2rfc.ietf.org/public/rfc/'))"/>
+            </xsl:when>
+            <xsl:otherwise>-->
+              <xsl:value-of select="@href"/>
+            <!--</xsl:otherwise>
+          </xsl:choose>-->
+        </xsl:variable>
+        <xsl:variable name="doc">
+          <xsl:copy-of select="document($uri)"/>
+        </xsl:variable>
+        <xsl:if test="count(exslt:node-set($doc)) = 1">
+          <myns:include from="{@href}" in="{generate-id(..)}">
+            <xsl:copy-of select="$doc"/>
+          </myns:include>
+        </xsl:if>
+        <xsl:for-each select="exslt:node-set($doc)//xi:include">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="'Nested x:include elements are not supported'"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:variable name="includeDirectives">
+  <xsl:call-template name="getIncludes">
+    <xsl:with-param name="nodes" select="/rfc/back//references/processing-instruction('rfc')|/rfc/back//references/referencegroup/processing-instruction('rfc')"/>
+  </xsl:call-template>
+  <xsl:call-template name="getXIncludes">
+    <xsl:with-param name="nodes" select="/rfc/back//references/xi:include|/rfc/back//references/referencegroup/xi:include"/>
+  </xsl:call-template>
+</xsl:variable>
+
+<xsl:variable name="sourcedReferences">
+  <xsl:for-each select="//reference[x:source/@href and not(seriesInfo)]">
+    <xsl:copy>
+      <xsl:variable name="f" select="document(x:source/@href)"/>
+      <xsl:if test="$f/rfc/@number" myns:namespaceless-elements="xml2rfc">
+        <seriesInfo name="RFC" value="{$f/rfc/@number}"/>
+      </xsl:if>
+      <xsl:if test="$f/rfc/@docName" myns:namespaceless-elements="xml2rfc">
+        <seriesInfo name="Internet-Draft" value="{$f/@docName}"/>
+      </xsl:if>
+    </xsl:copy>
+  </xsl:for-each>
+</xsl:variable>
+
+<!-- logging -->
+
+<xsl:param name="xml2rfc-ext-log-level">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'log-level'"/>
+    <xsl:with-param name="default" select="'WARNING'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:variable name="log-level">
+  <xsl:call-template name="parse-log-level">
+    <xsl:with-param name="level" select="$xml2rfc-ext-log-level"/>
+  </xsl:call-template>
+</xsl:variable>
+
+<xsl:param name="xml2rfc-ext-abort-on">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'abort-on'"/>
+    <xsl:with-param name="default" select="'OFF'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:variable name="abort-log-level">
+  <xsl:call-template name="parse-log-level">
+    <xsl:with-param name="level" select="$xml2rfc-ext-abort-on"/>
+  </xsl:call-template>
+</xsl:variable>
+
+<xsl:template name="parse-log-level">
+  <xsl:param name="level"/>
+  <xsl:choose>
+    <xsl:when test="$level='OFF'">6</xsl:when>
+    <xsl:when test="$level='FATAL'">5</xsl:when>
+    <xsl:when test="$level='ERROR'">4</xsl:when>
+    <xsl:when test="$level='WARNING'">3</xsl:when>
+    <xsl:when test="$level='INFO'">2</xsl:when>
+    <xsl:when test="$level='DEBUG'">1</xsl:when>
+    <xsl:when test="$level='TRACE'">0</xsl:when>
+    <xsl:otherwise>
+      <xsl:message>Unsupported LOG level '<xsl:value-of select="$level"/>', defaulting to 'WARNING'</xsl:message>
+      <xsl:value-of select="'3'"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- prettyprinting -->
+
+<xsl:param name="xml2rfc-ext-html-pretty-print">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'html-pretty-print'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:variable name="prettyprint-class">
+  <xsl:if test="$xml2rfc-ext-html-pretty-print">
+    <xsl:value-of select="substring-before(normalize-space($xml2rfc-ext-html-pretty-print),' ')"/>
+  </xsl:if>
+</xsl:variable>
+
+<xsl:variable name="prettyprint-script">
+  <xsl:if test="$xml2rfc-ext-html-pretty-print">
+    <xsl:value-of select="substring-after(normalize-space($xml2rfc-ext-html-pretty-print),' ')"/>
+  </xsl:if>
+</xsl:variable>
+
+<!-- Unicode database -->
+<xsl:param name="xml2rfc-ext-ucd-file">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'ucd-file'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- external resource containing errata, as generated by parse-errata.xslt -->
+<xsl:param name="xml2rfc-ext-errata"/>
+<xsl:variable name="errata-parsed" select="document($xml2rfc-ext-errata)//erratum[@status!='Rejected']"/>
+
+<!-- "remove in RFC phrases" -->
+<xsl:variable name="note-removeInRFC">This note is to be removed before publishing as an RFC.</xsl:variable>
+<xsl:variable name="section-removeInRFC">This section is to be removed before publishing as an RFC.</xsl:variable>
+
+<!-- constant string for unnumbered parts -->
+<xsl:variable name="unnumbered">unnumbered-</xsl:variable>
+
+<!-- CSS class name remapping -->
+
+<xsl:param name="xml2rfc-ext-css-map"/>
+
+<xsl:template name="generate-css-class">
+  <xsl:param name="name"/>
+  <xsl:variable name="cssmap" select="document($xml2rfc-ext-css-map)"/>
+  <xsl:variable name="entry" select="$cssmap/*/map[@from=$name]"/>
+  <xsl:choose>
+    <xsl:when test="$entry">
+      <xsl:value-of select="$entry/@css"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$name"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- WORK IN PROGRESS; ONLY A FEW CLASSES SUPPORTED FOR NOW -->
+<xsl:variable name="css-artwork"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'artwork'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-art-svg"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'art-svg'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-docstatus"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'docstatus'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-center"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'center'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-erratum"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'erratum'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-error"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'error'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-fbbutton"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'fbbutton'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-feedback"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'feedback'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-header"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'header'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-left"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'left'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-noprint"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'noprint'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-publishedasrfc"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'publishedasrfc'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-reference"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'reference'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-right"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'right'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tcenter"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tcenter'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tleft"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tleft'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tright"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tright'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tt"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tt'"/></xsl:call-template></xsl:variable>
+
+
+<!-- RFC-Editor site linking -->
+
+<xsl:param name="xml2rfc-ext-link-rfc-to-info-page">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'link-rfc-to-info-page'"/>
+    <xsl:with-param name="default">
+      <xsl:choose>
+        <xsl:when test="$pub-yearmonth >= 201503">yes</xsl:when>
+        <xsl:otherwise>no</xsl:otherwise>
+      </xsl:choose>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- DOI insertion -->
+
+<xsl:param name="xml2rfc-ext-insert-doi">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'insert-doi'"/>
+    <xsl:with-param name="default">
+      <xsl:choose>
+        <xsl:when test="$pub-yearmonth >= 201505">yes</xsl:when>
+        <xsl:otherwise>no</xsl:otherwise>
+      </xsl:choose>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- initials handling? -->
+
+<xsl:param name="xml2rfc-multiple-initials">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc')"/>
+    <xsl:with-param name="attr" select="'multiple-initials'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- paragraph links? -->
+
+<xsl:param name="xml2rfc-ext-paragraph-links">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'paragraph-links'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for XML parsing in artwork -->
+
+<xsl:param name="xml2rfc-ext-parse-xml-in-artwork">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'parse-xml-in-artwork'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-trace-parse-xml">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'trace-parse-xml'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for excluding the index -->
+
+<xsl:param name="xml2rfc-ext-include-index">
+  <xsl:choose>
+    <xsl:when test="/rfc/@indexInclude='false'">no</xsl:when>
+    <xsl:when test="/rfc/@indexInclude='true'">yes</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+        <xsl:with-param name="attr" select="'include-index'"/>
+        <xsl:with-param name="default" select="'yes'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- extension for inserting RFC metadata -->
+
+<xsl:param name="xml2rfc-ext-insert-metadata">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'insert-metadata'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for excluding DCMI properties in meta tag (RFC2731) -->
+
+<xsl:param name="xml2rfc-ext-support-rfc2731">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'support-rfc2731'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for excluding generator information -->
+
+<xsl:param name="xml2rfc-ext-include-generator">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'include-generator'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for specifying the value for <vspace> after which it's taken as a page break -->
+
+<xsl:param name="xml2rfc-ext-vspace-pagebreak">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'vspace-pagebreak'"/>
+    <xsl:with-param name="default" select="'100'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for allowing markup inside artwork -->
+
+<xsl:param name="xml2rfc-ext-allow-markup-in-artwork">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'allow-markup-in-artwork'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for including references into index -->
+
+<xsl:param name="xml2rfc-ext-include-references-in-index">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'include-references-in-index'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for switching the behaviour for xrefs with text content -->
+<!-- 'text': as in text output, 'nothing': just the link -->
+
+<xsl:param name="xml2rfc-ext-xref-with-text-generate">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'xref-with-text-generate-text'"/>
+    <xsl:with-param name="default" select="'text'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- position of author's section -->
+
+<xsl:param name="xml2rfc-ext-authors-section">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'authors-section'"/>
+    <xsl:with-param name="default" select="'end'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- justification? -->
+
+<xsl:param name="xml2rfc-ext-justification">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'justification'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- switch for doublesided layout -->
+
+<xsl:param name="xml2rfc-ext-duplex">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'duplex'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- trailing dots in section numbers -->
+
+<xsl:param name="xml2rfc-ext-sec-no-trailing-dots">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'sec-no-trailing-dots'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- check artwork width? -->
+
+<xsl:param name="xml2rfc-ext-check-artwork-width">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'check-artwork-width'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- choose whether or not to do mailto links -->
+
+<xsl:param name="xml2rfc-linkmailto">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'linkmailto'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- iprnotified switch -->
+
+<xsl:param name="xml2rfc-iprnotified">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'iprnotified'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- URL templates for RFCs and Internet Drafts. -->
+
+<!-- Reference the marked up versions over on https://tools.ietf.org/html. -->
+<xsl:param name="rfcUrlFragSection" select="'section-'" />
+<xsl:param name="rfcUrlFragAppendix" select="'appendix-'" />
+<xsl:param name="internetDraftUrlFragSection" select="'section-'" />
+<xsl:param name="internetDraftUrlFragAppendix" select="'appendix-'" />
+
+<!-- base URI for include directive when relative reference does not resolve for RFCs -->
+<xsl:param name="toolsBaseUriForRFCReferences">https://xml2rfc.tools.ietf.org/public/rfc/bibxml/</xsl:param>
+
+<!-- base URI for include directive when relative reference does not resolve for Intetnet Drafts -->
+<xsl:param name="toolsBaseUriForIDReferences">https://xml2rfc.tools.ietf.org/public/rfc/bibxml-ids/</xsl:param>
+
+<!--templates for URI calculation -->
+
+<xsl:param name="xml2rfc-ext-isbn-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'isbn-uri'"/>
+    <xsl:with-param name="default">https://www.worldcat.org/search?q=isbn:{isbn}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-isbn-uri">
+  <xsl:param name="isbn"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-isbn-uri"/>
+    <xsl:with-param name="replace" select="'{isbn}'"/>
+    <xsl:with-param name="by" select="translate($isbn,'-','')"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-uri'"/>
+    <!-- previously 'https://tools.ietf.org/html/rfc{rfc}' -->
+    <xsl:with-param name="default">https://www.rfc-editor.org/rfc/rfc{rfc}.html</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-rfc-uri">
+  <xsl:param name="rfc"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-rfc-uri"/>
+    <xsl:with-param name="replace" select="'{rfc}'"/>
+    <xsl:with-param name="by" select="$rfc"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-internet-draft-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'internet-draft-uri'"/>
+    <xsl:with-param name="default">https://tools.ietf.org/html/{internet-draft}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-internet-draft-uri">
+  <xsl:param name="internet-draft"/>
+  <xsl:param name="ref" select="."/>
+  <xsl:variable name="local-link-template">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="$ref/processing-instruction('rfc-ext')"/>
+      <xsl:with-param name="attr" select="'internet-draft-uri'"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$local-link-template!=''">
+      <xsl:call-template name="replace-substring">
+        <xsl:with-param name="string" select="$local-link-template"/>
+        <xsl:with-param name="replace" select="'{internet-draft}'"/>
+        <xsl:with-param name="by" select="$internet-draft"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="replace-substring">
+        <xsl:with-param name="string" select="$xml2rfc-ext-internet-draft-uri"/>
+        <xsl:with-param name="replace" select="'{internet-draft}'"/>
+        <xsl:with-param name="by" select="$internet-draft"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-diff-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'diff-uri'"/>
+    <xsl:with-param name="default">https://tools.ietf.org/rfcdiff?url2={internet-draft}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-diff-uri">
+  <xsl:param name="name"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-diff-uri"/>
+    <xsl:with-param name="replace" select="'{internet-draft}'"/>
+    <xsl:with-param name="by" select="$name"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-latest-diff-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'latest-diff-uri'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-latest-diff-uri">
+  <xsl:param name="name"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-latest-diff-uri"/>
+    <xsl:with-param name="replace" select="'{internet-draft}'"/>
+    <xsl:with-param name="by" select="$name"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-doi-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'doi-uri'"/>
+    <xsl:with-param name="default">https://dx.doi.org/{doi}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-doi-uri">
+  <xsl:param name="doi"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-doi-uri"/>
+    <xsl:with-param name="replace" select="'{doi}'"/>
+    <xsl:with-param name="by" select="$doi"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-info-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-info-uri'"/>
+    <xsl:with-param name="default">
+      <xsl:choose>
+        <xsl:when test="$pub-yearmonth &lt; 201708">http://www.rfc-editor.org/info/{type}{no}</xsl:when>
+        <xsl:otherwise>https://www.rfc-editor.org/info/{type}{no}</xsl:otherwise>
+      </xsl:choose>    
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-rfc-info-uri">
+  <xsl:param name="type"/>
+  <xsl:param name="no"/>
+  <xsl:variable name="t">
+    <xsl:call-template name="replace-substring">
+      <xsl:with-param name="string" select="$xml2rfc-ext-rfc-info-uri"/>
+      <xsl:with-param name="replace" select="'{type}'"/>
+      <xsl:with-param name="by" select="$type"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$t"/>
+    <xsl:with-param name="replace" select="'{no}'"/>
+    <xsl:with-param name="by" select="$no"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-erratum-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-erratum-uri'"/>
+    <xsl:with-param name="default">https://www.rfc-editor.org/errata/eid{eid}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-rfc-erratum-uri">
+  <xsl:param name="eid"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-rfc-erratum-uri"/>
+    <xsl:with-param name="replace" select="'{eid}'"/>
+    <xsl:with-param name="by" select="$eid"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-errata-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-errata-uri'"/>
+    <xsl:with-param name="default">https://www.rfc-editor.org/errata/rfc{rfc}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-draft-status-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'draft-status-uri'"/>
+    <xsl:with-param name="default">https://datatracker.ietf.org/doc/{draftname}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-draft-status-uri">
+  <xsl:param name="draftname"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-draft-status-uri"/>
+    <xsl:with-param name="replace" select="'{draftname}'"/>
+    <xsl:with-param name="by" select="$draftname"/>
+  </xsl:call-template>
+</xsl:template>
+
+<!-- the format we're producing -->
+<xsl:param name="outputExtension" select="'html'"/>
+
+<!-- source for autorefresh -->
+<xsl:param name="xml2rfc-ext-refresh-from">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'refresh-from'"/>
+    <xsl:with-param name="default" select="''"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- XSLT for autorefresh -->
+<xsl:param name="xml2rfc-ext-refresh-xslt">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'refresh-xslt'"/>
+    <xsl:with-param name="default" select="'rfc2629.xslt'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- interval for autorefresh -->
+<xsl:param name="xml2rfc-ext-refresh-interval">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'refresh-interval'"/>
+    <xsl:with-param name="default" select="10"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- for testing: switch to disable code that gets the system time -->
+<xsl:param name="xml2rfc-ext-use-system-time">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'use-system-time'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- warning re: absent node-set ext. function -->
+<xsl:variable name="node-set-warning">
+  This stylesheet requires either an XSLT-1.0 processor with node-set()
+  extension function, or an XSLT-2.0 processor. Therefore, parts of the
+  document couldn't be displayed.
+</xsl:variable>
+
+<!-- character translation tables -->
+<xsl:variable name="lcase" select="'abcdefghijklmnopqrstuvwxyz'" />
+<xsl:variable name="ucase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
+<xsl:variable name="digits" select="'0123456789'" />
+<xsl:variable name="alpha" select="concat($lcase,$ucase)"/>
+<xsl:variable name="alnum" select="concat($alpha,$digits)"/>
+
+<!-- build help keys for indices -->
+<xsl:key name="index-first-letter"
+  match="iref|reference"
+    use="translate(substring(concat(/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor,@item),1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
+
+<xsl:key name="index-item"
+  match="iref"
+    use="@item" />
+
+<xsl:key name="index-item-subitem"
+  match="iref"
+    use="concat(@item,'..',@subitem)" />
+
+<xsl:key name="index-xref-by-sec"
+  match="xref[@x:sec]|relref[@section]"
+    use="concat(@target,'..',@x:sec,@section)" />
+
+<xsl:key name="index-xref-by-anchor"
+  match="xref[@x:rel]|relref[@relative]"
+    use="concat(@target,'..',@x:rel,@relative)" />
+
+<xsl:key name="anchor-item"
+  match="//*[@anchor]"
+    use="@anchor"/>
+
+<xsl:key name="xref-item"
+  match="//xref|//relref"
+    use="@target"/>
+
+<xsl:key name="extref-item"
+  match="//x:ref"
+    use="."/>
+
+<!-- prefix for automatically generated anchors -->
+<xsl:variable name="anchor-pref" select="'rfc.'" />
+
+<!-- IPR version switch -->
+<xsl:variable name="ipr-rfc3667" select="(
+  number($rfcno) &gt; 3708) or
+  not(
+    (/rfc/@ipr = 'full2026') or
+    (/rfc/@ipr = 'noDerivativeWorks2026') or
+    (/rfc/@ipr = 'noDerivativeWorksNow') or
+    (/rfc/@ipr = 'none') or
+    (/rfc/@ipr = '') or
+    not(/rfc/@ipr)
+  )" />
+
+<xsl:variable name="draft-fullname" select="/rfc/@docName"/>
+
+<xsl:variable name="draft-seq">
+  <xsl:call-template name="draft-sequence-number">
+    <xsl:with-param name="name" select="$draft-fullname"/>
+  </xsl:call-template>
+</xsl:variable>
+
+<xsl:variable name="draft-basename">
+  <xsl:call-template name="draft-base-name">
+    <xsl:with-param name="name" select="$draft-fullname"/>
+  </xsl:call-template>
+</xsl:variable>
+
+<xsl:variable name="is-submitted-draft" select="number($draft-seq)=$draft-seq"/>
+
+<xsl:variable name="is-rfc" select="$src/rfc/@number"/>
+
+<xsl:variable name="rfcno">
+  <xsl:value-of select="$src/rfc/@number"/>
+  <xsl:if test="$is-rfc">
+    <xsl:for-each select="$src/rfc/front/seriesInfo[@name='RFC']">
+      <xsl:if test="number(@value) != number($src/rfc/@number)">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">RFC number given in /rfc/front/seriesInfo (<xsl:value-of select="@value"/>) inconsistent with rfc element (<xsl:value-of select="$src/rfc/@number"/>)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:for-each>
+  </xsl:if>
+</xsl:variable>
+
+<xsl:variable name="submissionType">
+  <xsl:choose>
+    <xsl:when test="/rfc/@submissionType='IETF' or not(/rfc/@submissionType) or /rfc/submissionType=''">IETF</xsl:when>
+    <xsl:when test="/rfc/@submissionType='IAB' or /rfc/@submissionType='IRTF' or /rfc/@submissionType='independent'">
+      <xsl:value-of select="/rfc/@submissionType"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat('(UNSUPPORTED SUBMISSION TYPE: ',/rfc/@submissionType,')')"/>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported value for /rfc/@submissionType: ', /rfc/@submissionType)"/>
+        <xsl:with-param name="inline" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <!-- sanity check on @consensus -->
+  <xsl:if test="/rfc/@consensus and /rfc/@submissionType='independent'">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg" select="concat('/rfc/@consensus meaningless with a /rfc/@submissionType value of ', /rfc/@submissionType)"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:variable>
+
+<xsl:variable name="consensus">
+  <xsl:choose>
+    <xsl:when test="/rfc/@consensus='yes' or /rfc/@consensus='true' or not(/rfc/@consensus)">yes</xsl:when>
+    <xsl:when test="/rfc/@consensus='no' or /rfc/@consensus='false'">no</xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat('(UNSUPPORTED VALUE FOR CONSENSUS: ',/rfc/@consensus,')')"/>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported value for /rfc/@consensus: ', /rfc/@consensus)"/>
+        <xsl:with-param name="inline" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- Header format as defined in RFC 5741, and deployed end of Dec 2009 -->
+<xsl:variable name="header-format">
+  <xsl:choose>
+    <xsl:when test="$pub-yearmonth >= 201001 or
+      ($rfcno=5741 or $rfcno=5742 or $rfcno=5743)"
+      >2010</xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="rfc-boilerplate">
+  <xsl:choose>
+    <!-- RFC boilerplate as defined in RFC 5741, and deployed end of Dec 2009 -->
+    <xsl:when test="$pub-yearmonth >= 201001 or
+      ($rfcno=5741 or $rfcno=5742 or $rfcno=5743)"
+      >2010</xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- use https in boilerplate links? -->
+<xsl:variable name="rfc-boilerplate-use-https" select="($pub-yearmonth >= 201709 and number($rfcno)!=8230 and number($rfcno)!=8325 and number($rfcno)!=8236) or number($rfcno)=8214 or number($rfcno)=8223"/>
+
+<xsl:variable name="rfc-info-link">
+  <xsl:variable name="scheme">
+    <xsl:choose>
+      <xsl:when test="$rfc-boilerplate-use-https">https</xsl:when>
+      <xsl:otherwise>http</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="concat($scheme,'://www.rfc-editor.org/info/rfc',$rfcno)"/>
+</xsl:variable>
+
+<xsl:variable name="trust-license-info-link">
+  <xsl:variable name="scheme">
+    <xsl:choose>
+      <xsl:when test="$rfc-boilerplate-use-https">https</xsl:when>
+      <xsl:otherwise>http</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="concat($scheme,'://trustee.ietf.org/license-info')"/>
+</xsl:variable>
+
+<!-- the reference to the latest and greatest headers-and-boilerplates document -->
+<xsl:variable name="hab-reference" myns:namespaceless-elements="xml2rfc">
+  <eref>
+    <xsl:choose>
+      <xsl:when test="$pub-yearmonth >= 201606 or ($rfcno=7846 or $rfcno=7865 or $rfcno=7866 or $rfcno=7873 or $rfcno=7879 or $rfcno=7892)"><xsl:attribute name="target">https://tools.ietf.org/html/rfc7841#section-2</xsl:attribute>Section 2 of RFC 7841</xsl:when>
+      <xsl:otherwise><xsl:attribute name="target">https://tools.ietf.org/html/rfc5741#section-2</xsl:attribute>Section 2 of RFC 5741</xsl:otherwise>
+    </xsl:choose>
+  </eref>
+</xsl:variable>
+
+<xsl:variable name="id-boilerplate">
+  <xsl:choose>
+    <!-- ID boilerplate approved by IESG on Jan 14 2010-->
+    <xsl:when test="$pub-yearmonth >= 201004"
+      >2010</xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="ipr-rfc4748" select="(
+  $ipr-rfc3667 and
+    ( number($rfcno) &gt;= 4715 and ( number($rfcno)!=4718 and number($rfcno)!=4735 and number($rfcno)!= 4749 ))
+    or
+    ( number($rfcno)=4578 or number($rfcno)=4582 or number($rfcno)=4583 or number($rfcno)=4628 or number($rfcno)=4629 or number($rfcno)=4639 or number($rfcno)=4651 or number($rfcno)=4682 or number($rfcno)=4684 or number($rfcno)=4695 or number($rfcno)=4696 )
+    or
+    ( not($is-rfc) and $pub-yearmonth >= 200611)
+  )" />
+
+<xsl:variable name="ipr-2007-08" select="(
+  $ipr-rfc4748 and
+    (
+      (number($rfcno) &gt; 5000
+        and number($rfcno) != 5020
+        and number($rfcno) != 5021
+        and number($rfcno) != 5034
+        and number($rfcno) != 5052
+        and number($rfcno) != 5065
+        and number($rfcno) != 5094) or
+      ($xml2rfc-ext-pub-year >= 2008) or
+      (not($is-rfc) and $pub-yearmonth >= 200709)
+    )
+  )" />
+
+<xsl:variable name="ipr-2008-11" select="(
+    $is-rfc and $pub-yearmonth >= 200811
+  )
+  or
+  (
+    /rfc/@ipr = 'trust200811' or
+    /rfc/@ipr = 'noModificationTrust200811' or
+    /rfc/@ipr = 'noDerivativesTrust200902' or
+    /rfc/@ipr = 'trust200902' or
+    /rfc/@ipr = 'noModificationTrust200902' or
+    /rfc/@ipr = 'noDerivativesTrust200902' or
+    /rfc/@ipr = 'pre5378Trust200902'
+  )" />
+
+<xsl:variable name="ipr-2009-02" select="(
+    $ipr-2008-11 and $pub-yearmonth >= 200902
+  )" />
+
+<!-- this makes the Sep 2009 TLP text depend on the publication date to be >= 2009-11
+     for IDs, and around 2009-09 for RFCs-->
+<xsl:variable name="ipr-2009-09" select="(
+    ( not($is-rfc) and $pub-yearmonth >= 200911 )
+    or
+    (
+      $is-rfc and $pub-yearmonth >= 200909 and
+      $rfcno!=5582 and $rfcno!=5621 and $rfcno!=5632 and $rfcno!=5645 and $rfcno!=5646 and $rfcno!=5681
+    )
+  )" />
+
+<!-- this makes the Jan 2010 TLP text depend on the publication date to be >= 2010-04
+     for IDs, and around 2010-01 for RFCs-->
+<xsl:variable name="ipr-2010-01" select="(
+    ( not($is-rfc) and $pub-yearmonth >= 201004 )
+    or
+    (
+      $is-rfc and ($pub-yearmonth >= 201001 or
+      $rfcno=5741 or $rfcno=5742 or $rfcno=5743)
+    )
+  )" />
+
+<!-- see http://mailman.rfc-editor.org/pipermail/rfc-interest/2009-June/001373.html -->
+<!-- for IDs, implement the change as 2009-11 -->
+<xsl:variable name="abstract-first" select="(
+    ($is-rfc and $pub-yearmonth >= 200907)
+    or
+    (not($is-rfc) and $pub-yearmonth >= 200911)
+  )" />
+
+<!-- RFC 7322 changed the placement of notes -->
+<xsl:variable name="notes-follow-abstract" select="(
+    ($is-rfc and $rfcno >= 7200)
+    or
+    ($pub-yearmonth >= 201409)
+  )" />
+
+<!-- funding switch -->
+<xsl:variable name="funding0" select="(
+  $rfcno &gt; 2499) or
+  (not($is-rfc) and /rfc/@docName and $xml2rfc-ext-pub-year &gt;= 1999
+  )" />
+
+<xsl:variable name="funding1" select="(
+  $rfcno &gt; 4320) or
+  (not($is-rfc) and /rfc/@docName and $xml2rfc-ext-pub-year &gt;= 2006
+  )" />
+
+<xsl:variable name="no-funding" select="$ipr-2007-08"/>
+
+<xsl:variable name="no-copylong" select="$ipr-2008-11 or /rfc/@ipr='none'"/>
+
+<!-- will document have an index -->
+<xsl:variable name="has-index" select="(//iref or (//xref and $xml2rfc-ext-include-references-in-index='yes')) and $xml2rfc-ext-include-index!='no'" />
+
+<!-- does the document contain edits? -->
+<xsl:variable name="has-edits" select="//ed:ins | //ed:del | //ed:replace" />
+
+<!-- does the document have a published-as-rfc link? -->
+<xsl:variable name="published-as-rfc" select="/*/x:link[@rel='Alternate' and starts-with(@title,'RFC')]"/>
+
+
+<xsl:template match="text()[not(ancestor::artwork or ancestor::sourcecode)]">
+  <xsl:variable name="ws" select="'&#9;&#10;&#13;&#32;'"/>
+  <xsl:variable name="starts-with-ws" select="'' = translate(substring(.,1,1),$ws,'')"/>
+  <xsl:variable name="ends-with-ws" select="'' = translate(substring(.,string-length(.),1),$ws,'')"/>
+  <xsl:variable name="normalized" select="normalize-space(.)"/>
+  <!--<xsl:message> Orig: "<xsl:value-of select="."/>"</xsl:message>
+  <xsl:message>Start: "<xsl:value-of select="$starts-with-ws"/>"</xsl:message>
+  <xsl:message>  End: "<xsl:value-of select="$ends-with-ws"/>"</xsl:message> -->
+  <xsl:if test="$starts-with-ws">
+    <xsl:variable name="t">
+      <xsl:for-each select="preceding-sibling::node()">
+        <xsl:choose>
+          <xsl:when test="self::text()">
+            <xsl:value-of select="."/>
+          </xsl:when>
+          <xsl:when test="self::*">
+            <xsl:apply-templates select="."/>
+          </xsl:when>
+          <xsl:otherwise/>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:variable name="text-before" select="normalize-space($t)"/>
+    <xsl:if test="$text-before!=''">
+      <xsl:text> </xsl:text>
+    </xsl:if>
+  </xsl:if>
+  <xsl:value-of select="$normalized"/>
+  <xsl:if test="$ends-with-ws and $normalized!=''">
+    <xsl:variable name="t">
+      <xsl:for-each select="following-sibling::node()">
+        <xsl:choose>
+          <xsl:when test="self::text()">
+            <xsl:value-of select="."/>
+          </xsl:when>
+          <xsl:when test="self::*">
+            <xsl:apply-templates select="."/>
+          </xsl:when>
+          <xsl:otherwise/>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:variable name="text-after" select="normalize-space($t)"/>
+    <xsl:if test="$text-after!='' and substring($t,1,1)!=' '">
+      <xsl:text> </xsl:text>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="abstract">
+  <xsl:call-template name="check-no-text-content"/>
+  <section>
+    <xsl:call-template name="copy-anchor"/>
+    <h2 id="{$anchor-pref}abstract"><a href="#{$anchor-pref}abstract">Abstract</a></h2>
+    <xsl:call-template name="insert-errata">
+      <xsl:with-param name="section" select="'abstract'"/>
+    </xsl:call-template>
+    <xsl:apply-templates />
+  </section>
+</xsl:template>
+
+<msxsl:script language="JScript" implements-prefix="myns">
+  function parseXml(str) {
+    try {
+      var doc = new ActiveXObject("MSXML2.DOMDocument");
+      doc.async = false;
+      if (doc.loadXML(str)) {
+        return "";
+      }
+      else {
+        return doc.parseError.reason + "\n" + doc.parseError.srcText + " (" + doc.parseError.line + "/" + doc.parseError.linepos + ")";
+      }
+    }
+    catch(e) {
+      return "";
+    }
+  }
+</msxsl:script>
+
+<xsl:template name="add-artwork-class">
+  <xsl:variable name="v">
+    <xsl:choose>
+      <xsl:when test="@type='abnf' or @type='abnf2045' or @type='abnf2616' or @type='abnf7230' or @type='application/xml-dtd' or @type='inline' or @type='application/relax-ng-compact-syntax'">inline</xsl:when>
+      <xsl:when test="starts-with(@type,'message/http') and contains(@type,'msgtype=&quot;request&quot;')">text2</xsl:when>
+      <xsl:when test="starts-with(@type,'message/http')">text</xsl:when>
+      <xsl:when test="@type='drawing' or @type='pdu'">drawing</xsl:when>
+      <xsl:when test="self::sourcecode or @type='text/plain' or @type='example' or @type='code' or @type='xml' or @type='application/xml-dtd' or @type='application/json'">text</xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+    <xsl:if test="@x:lang and $prettyprint-class!=''">
+      <xsl:value-of select="concat(' ',$prettyprint-class)"/>
+      <xsl:if test="@x:lang!=''">
+        <xsl:value-of select="concat(' lang-',@x:lang)"/>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains(@type,'abnf') and $prettyprint-class!=''">
+      <xsl:value-of select="concat(' ',$prettyprint-class,' lang-ietf_abnf')"/>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:if test="normalize-space($v)!=''">
+    <xsl:attribute name="class"><xsl:value-of select="normalize-space($v)"/></xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insert-begin-code">
+  <xsl:if test="(self::artwork and @x:is-code-component='yes') or (self::sourcecode and @markers='true')">
+    <pre class="ccmarker cct">
+      <xsl:text>&lt;CODE BEGINS></xsl:text>
+      <xsl:if test="self::sourcecode and @name">
+        <xsl:variable name="offending" select="translate(@name,concat($alnum,'-+.,;_~#'),'')"/>
+        <xsl:choose>
+          <xsl:when test="$offending!=''">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">illegal characters in @name attribute '<xsl:value-of select="@name"/>': '<xsl:value-of select="$offending"/>'</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:text> file "</xsl:text>
+            <xsl:value-of select="@name"/>
+            <xsl:text>"</xsl:text>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </pre>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insert-end-code">
+  <xsl:if test="(self::artwork and @x:is-code-component='yes') or (self::sourcecode and @markers='true')">
+    <pre class="ccmarker ccb">&lt;CODE ENDS></pre>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="artset">
+  <xsl:call-template name="check-no-text-content"/>
+  <!-- see https://tools.ietf.org/html/draft-levkowetz-xml2rfc-v3-implementation-notes-08#section-3.1.1 -->
+  <xsl:choose>
+    <xsl:when test="artwork[svg:svg or normalize-space(.)='' or @src!='']">
+      <xsl:apply-templates select="artwork[svg:svg or normalize-space(.)='' or @src!=''][1]"/>
+    </xsl:when>
+    <xsl:when test="artwork">
+      <xsl:apply-templates select="artwork[1]"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">artset needs to contain at least one artwork child element</xsl:with-param>
+      </xsl:call-template>
+      <p>
+        <xsl:call-template name="attach-paragraph-number-as-id"/>
+        <xsl:if test="@anchor">
+          <span id="{@anchor}"/>
+        </xsl:if>
+      </p>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="artwork|sourcecode">
+  <xsl:if test="not(ancestor::ed:del) and $xml2rfc-ext-parse-xml-in-artwork='yes' and function-available('myns:parseXml')" use-when="function-available('myns:parseXml')">
+    <xsl:if test="contains(.,'&lt;?xml')">
+      <xsl:variable name="body" select="substring-after(substring-after(.,'&lt;?xml'),'?>')" />
+      <xsl:if test="$body!='' and myns:parseXml($body)!=''">
+        <table style="background-color: red; border-width: thin; border-style: solid; border-color: black;">
+        <tr><td>
+        XML PARSE ERROR; parsed the body below:
+        <pre>
+        <xsl:value-of select="$body"/>
+        </pre>
+        resulting in:
+        <pre>
+        <xsl:value-of select="myns:parseXml($body)" />
+        </pre>
+        </td></tr></table>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="@ed:parse-xml-after">
+      <xsl:if test="myns:parseXml(string(.))!=''">
+        <table style="background-color: red; border-width: thin; border-style: solid; border-color: black;">
+        <tr><td>
+        XML PARSE ERROR:
+        <pre><xsl:value-of select="myns:parseXml(string(.))" /></pre>
+        </td></tr></table>
+      </xsl:if>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="contains(.,'&#9;')">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg" select="'artwork or sourcecode contains HTAB character'"/>
+      <xsl:with-param name="inline" select="'no'"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:variable name="display">
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-allow-markup-in-artwork='yes'">
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="text-in-artwork"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="divstyle">
+    <xsl:choose>
+      <xsl:when test="self::artwork and @align='right'">display:table; margin-left: auto; margin-right: 0em;</xsl:when>
+      <xsl:when test="self::artwork and @align='center'">display:table; margin-left: auto; margin-right: auto;</xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="prestyle">
+    <xsl:choose>
+      <xsl:when test="self::artwork and (@align='right' or @align='center')">margin-left: 0em;</xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <div>
+    <xsl:choose>
+      <xsl:when test="parent::artset">
+        <xsl:for-each select="..">
+          <xsl:call-template name="attach-paragraph-number-as-id"/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="attach-paragraph-number-as-id"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="$divstyle!=''">
+      <xsl:attribute name="style"><xsl:value-of select="$divstyle"/></xsl:attribute>
+    </xsl:if>
+    <xsl:call-template name="insert-begin-code"/>
+    <pre>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:if test="$prestyle!=''">
+        <xsl:attribute name="style"><xsl:value-of select="$prestyle"/></xsl:attribute>
+      </xsl:if>
+      <xsl:call-template name="add-artwork-class"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:copy-of select="$display"/>
+    </pre>
+    <xsl:call-template name="insert-end-code"/>
+  </div>
+  <xsl:call-template name="check-artwork-width">
+    <xsl:with-param name="content"><xsl:apply-templates/></xsl:with-param>
+    <xsl:with-param name="indent"><xsl:value-of select="string-length(@x:indent-with)"/></xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="text-in-artwork">
+  <xsl:param name="content" select="."/>
+  <xsl:choose>
+    <xsl:when test="contains($content,'&#9;')">
+      <xsl:call-template name="text-in-artwork">
+        <xsl:with-param name="content" select="substring-before($content,'&#9;')"/>
+      </xsl:call-template>
+      <span class="error" title="HTAB character">&#x2409;</span>
+      <xsl:call-template name="text-in-artwork">
+        <xsl:with-param name="content" select="substring-after($content,'&#9;')"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$content"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- special case for first text node in artwork or sourcecode -->
+<xsl:template match="artwork/text()[1]|sourcecode/text()[1]" priority="9">
+  <xsl:choose>
+    <xsl:when test="starts-with(.,'&#10;')">
+      <!-- reduce leading whitespace -->
+      <xsl:call-template name="text-in-artwork">
+        <xsl:with-param name="content" select="substring(.,2)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="text-in-artwork"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- other text nodes in artwork or sourcecode -->
+<xsl:template match="artwork//text()|sourcecode//text()">
+  <xsl:call-template name="text-in-artwork"/>
+</xsl:template>
+
+<xsl:template name="check-artwork-width">
+  <xsl:param name="content"/>
+  <xsl:param name="indent"/>
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-check-artwork-width='no'">
+      <!-- skip check -->
+    </xsl:when>
+    <xsl:when test="not(contains($content,'&#10;'))">
+      <xsl:if test="string-length($content) > 69 + number($indent)">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">artwork line too long: '<xsl:value-of select="$content"/>' (<xsl:value-of select="string-length($content)"/> characters)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="start" select="substring-before($content,'&#10;')"/>
+      <xsl:variable name="end" select="substring-after($content,'&#10;')"/>
+      <xsl:variable name="max">
+        <xsl:choose>
+          <xsl:when test="$indent!=''"><xsl:value-of select="69 + $indent"/></xsl:when>
+          <xsl:otherwise>69</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <xsl:if test="string-length($start) > $max">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">artwork line too long: '<xsl:value-of select="$start"/>' (<xsl:value-of select="string-length($start)"/> characters)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="check-artwork-width">
+        <xsl:with-param name="content" select="$end"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="artwork[xi:include]" priority="9">
+  <xsl:variable name="resolved" xmlns="">
+    <xsl:element name="artwork" namespace="">
+      <xsl:copy-of select="@*"/>
+      <xsl:for-each select="node()">
+        <xsl:choose>
+          <xsl:when test="self::xi:include">
+            <xsl:if test="(@parse and @parse!='xml') or @xpointer">
+              <xsl:call-template name="error">
+                <xsl:with-param name="msg" select="'Unsupported attributes on x:include element'"/>
+              </xsl:call-template>
+            </xsl:if>
+            <xsl:copy-of select="document(@href)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:copy-of select="."/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:element>
+  </xsl:variable>
+  <xsl:apply-templates select="exslt:node-set($resolved)/*"/>
+</xsl:template>
+
+<xsl:template match="artwork[@src and starts-with(@type,'image/') or @type='svg']|artwork[svg:svg]">
+  <xsl:variable name="class">
+    <xsl:value-of select="$css-artwork"/>
+    <xsl:text> </xsl:text>
+    <xsl:if test="svg:svg">
+      <xsl:value-of select="$css-art-svg"/>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="@align='center'"><xsl:text> </xsl:text><xsl:value-of select="$css-center"/></xsl:when>
+      <xsl:when test="@align='right'"><xsl:text> </xsl:text><xsl:value-of select="$css-right"/></xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <div class="{normalize-space($class)}">
+    <xsl:choose>
+      <xsl:when test="parent::artset">
+        <xsl:for-each select="..">
+          <xsl:call-template name="attach-paragraph-number-as-id"/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="attach-paragraph-number-as-id"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="svg:svg">
+        <xsl:choose>
+          <xsl:when test="parent::artset and ../@anchor">
+            <div id="{../@anchor}">
+              <xsl:apply-templates select="svg:svg" mode="embed-svg"/>
+            </div>
+          </xsl:when>
+          <xsl:when test="parent::artset and ../artwork/@anchor">
+            <div id="{../artwork[@anchor][1]/@anchor}">
+              <xsl:apply-templates select="svg:svg" mode="embed-svg"/>
+            </div>
+          </xsl:when>
+          <xsl:when test="@anchor">
+            <div id="{@anchor}">
+              <xsl:apply-templates select="svg:svg" mode="embed-svg"/>
+            </div>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates select="svg:svg" mode="embed-svg"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="alt">
+          <xsl:choose>
+            <xsl:when test="@alt!=''">
+              <xsl:value-of select="@alt"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="."/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <img src="{@src}">
+          <xsl:if test="normalize-space($alt)!=''">
+            <xsl:attribute name="alt"><xsl:value-of select="$alt"/></xsl:attribute>
+          </xsl:if>
+          <xsl:if test="@width and @width!=''">
+            <xsl:copy-of select="@width"/>
+          </xsl:if>
+          <xsl:if test="@height and @height!=''">
+            <xsl:copy-of select="@height"/>
+          </xsl:if>
+        </img>
+      </xsl:otherwise>
+    </xsl:choose>
+  </div>
+</xsl:template>
+
+<!-- copy SVG content without inserted line no information -->
+<xsl:template match="node()|@*" mode="embed-svg">
+  <xsl:copy><xsl:apply-templates select="node()|@*" mode="embed-svg"/></xsl:copy>
+</xsl:template>
+<xsl:template match="processing-instruction('rfc-ext')[contains(.,'line-no=')]" mode="embed-svg"/>
+
+<xsl:template match="/" mode="embed-svg">
+	<xsl:copy><xsl:apply-templates select="node()"  mode="embed-svg"/></xsl:copy>
+</xsl:template>
+
+<xsl:template match="contact[ancestor::t]">
+  <xsl:if test="*">
+    <xsl:call-template name="info">
+      <xsl:with-param name="msg">Ignoring child elements of &lt;contact> when used inside &lt;t>.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:value-of select="@fullname"/>
+  <xsl:if test="@asciiFullname">
+    <xsl:text> (</xsl:text>
+    <xsl:value-of select="@asciiFullname"/>
+    <xsl:text>)</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="author|contact|x:contributor">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <address>
+    <xsl:call-template name="emit-author"/>
+
+    <xsl:if test="@asciiFullname!='' or organization/@ascii!='' or address/postal/*/@ascii">
+      <br/><br/>
+      <em>Additional contact information:</em>
+      <br/>
+      <xsl:call-template name="emit-author">
+        <xsl:with-param name="ascii" select="false()"/>
+      </xsl:call-template>
+    </xsl:if>
+  </address>
+</xsl:template>
+
+<xsl:template name="emit-postal-line">
+  <xsl:param name="prefix"/>
+  <xsl:param name="value"/>
+  <xsl:param name="values"/>
+  <xsl:param name="link"/>
+  <xsl:param name="annotation"/>
+
+  <xsl:if test="normalize-space($value)!='' or $values">
+    <br/>
+    <xsl:if test="$prefix!=''"><xsl:value-of select="$prefix"/>: </xsl:if>
+    <xsl:choose>
+      <xsl:when test="$values">
+        <xsl:for-each select="exslt:node-set($values)/*">
+          <xsl:choose>
+            <xsl:when test="@href">
+              <a href="{@href}"><xsl:value-of select="normalize-space(.)"/></a>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="normalize-space(.)"/>
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:if test="position()!=last()">, </xsl:if>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:when test="$link!=''">
+        <a href="{$link}"><xsl:value-of select="normalize-space($value)"/></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="normalize-space($value)"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="$annotation!=''">
+      <xsl:text> </xsl:text>
+      <i><xsl:value-of select="$annotation"/></i>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="emit-author-details">
+  <xsl:param name="ascii"/>
+  <xsl:for-each select="address">
+    <xsl:choose>
+      <xsl:when test="position() != 1">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Multiple &lt;address> elements inside &lt;author>, all but the first ignored.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="emit-author-details2">
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="emit-postal-city">
+  <xsl:param name="ascii"/>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value">
+      <xsl:if test="city">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="node" select="city"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-city-code">
+  <xsl:param name="ascii"/>
+  <xsl:param name="prefix"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value">
+      <xsl:value-of select="$city"/>
+      <xsl:text> </xsl:text>
+      <xsl:if test="$code!=''">
+        <xsl:choose>
+          <xsl:when test="$prefix!='' and starts-with($code,$prefix)">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Prefix '<xsl:value-of select="$prefix"/>' on &lt;code> '<xsl:value-of select="$code"/>' will be inserted automatically.</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$prefix"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="$code"/>
+      </xsl:if>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-city-region-code">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value" select="concat($city,' ',$region,' ',$code)"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-code-city-region">
+  <xsl:param name="ascii"/>
+  <xsl:param name="cr-delim" select="' '"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value" select="concat($code,' ',$city,$cr-delim,$region)"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-city-comma-region-code">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value">
+      <xsl:value-of select="$city"/>
+      <xsl:variable name="region-and-code" select="concat($region,' ',$code)"/>
+      <xsl:if test="normalize-space($region-and-code)!=''">
+        <xsl:if test="$city!=''">
+          <xsl:text>, </xsl:text>
+        </xsl:if>
+        <xsl:value-of select="normalize-space($region-and-code)"/>
+      </xsl:if>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-city-minus-region">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value">
+      <xsl:value-of select="$city"/>
+      <xsl:if test="$region!=''">
+        <xsl:if test="$city!=''">
+          <xsl:text>-</xsl:text>
+        </xsl:if>
+        <xsl:value-of select="$region"/>
+      </xsl:if>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-cityarea">
+  <xsl:param name="ascii"/>
+  <xsl:if test="cityarea">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="node" select="cityarea"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="emit-postal-cityarea-city">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="cityarea">
+    <xsl:if test="cityarea">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="cityarea"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value" select="concat($cityarea,' ',$city)"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-code">
+  <xsl:param name="ascii"/>
+  <xsl:param name="prefix"/>
+  <xsl:if test="code">
+    <xsl:variable name="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+      <xsl:if test="$code!=''">
+        <xsl:choose>
+          <xsl:when test="$prefix!='' and starts-with($code,$prefix)">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Prefix '<xsl:value-of select="$prefix"/>' on &lt;code> '<xsl:value-of select="$code"/>' will be inserted automatically.</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$prefix"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="$code"/>
+      </xsl:if>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="emit-postal-country">
+  <xsl:param name="ascii"/>
+  <xsl:if test="country">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="node" select="country"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="emit-postal-code-city">
+  <xsl:param name="ascii"/>
+  <xsl:param name="prefix"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value">
+      <xsl:if test="$code!=''">
+        <xsl:choose>
+          <xsl:when test="$prefix!='' and starts-with($code,$prefix)">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Prefix '<xsl:value-of select="$prefix"/>' on &lt;code> '<xsl:value-of select="$code"/>' will be inserted automatically.</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$prefix"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="$code"/>
+      </xsl:if>
+      <xsl:text> </xsl:text>
+      <xsl:value-of select="$city"/>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-region">
+  <xsl:param name="ascii"/>
+  <xsl:if test="region">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="node" select="region"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="emit-postal-region-city-cityarea">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="city">
+    <xsl:if test="city">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="city"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="cityarea">
+    <xsl:if test="cityarea">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="cityarea"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value" select="concat($region,' ',$city,' ',$cityarea)"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-region-code">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value" select="concat($region,' ',$code)"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-region-comma-code">
+  <xsl:param name="ascii"/>
+  <xsl:variable name="region">
+    <xsl:if test="region">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="region"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="code">
+    <xsl:if test="code">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="code"/>
+        <xsl:with-param name="ascii" select="$ascii"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:call-template name="emit-postal-line">
+    <xsl:with-param name="value">
+      <xsl:value-of select="$region"/>
+      <xsl:if test="$region!='' and $code!=''">, </xsl:if>
+      <xsl:value-of select="$code"/>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-postal-street">
+  <xsl:param name="ascii"/>
+  <xsl:for-each select="extaddr">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:for-each>
+  <xsl:for-each select="street">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:for-each>
+  <xsl:for-each select="pobox">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="value">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="author-name-for-diags">
+  <xsl:variable name="author" select="ancestor-or-self::author"/>
+  <xsl:choose>
+    <xsl:when test="$author/@fullname">
+      <xsl:value-of select="$author/@fullname"/>
+    </xsl:when>
+    <xsl:when test="$author/@surname">
+      <xsl:value-of select="$author/@surname"/>
+    </xsl:when>
+    <xsl:when test="$author/organization">
+      <xsl:text>(org) </xsl:text>
+      <xsl:value-of select="$author/organization"/>
+    </xsl:when>
+    <xsl:otherwise>???</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="emit-postal-warnings">
+  <xsl:param name="nodes"/>
+  <xsl:for-each select="$nodes">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">Element '<xsl:value-of select="local-name(.)"/>' with value '<xsl:value-of select="normalize-space(.)"/>' not displayed in postal address for '<xsl:call-template name="author-name-for-diags"/>'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="emit-author-details2">
+  <xsl:param name="ascii"/>
+  <xsl:for-each select="postal">
+    <xsl:choose>
+      <xsl:when test="position()!=1">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Multiple &lt;postal> elements inside &lt;address> for '<xsl:call-template name="author-name-for-diags"/>', all but the first ignored.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="not(postalLine)">
+      	<xsl:variable name="ascii-country">
+          <xsl:if test="country">
+            <xsl:call-template name="extract-normalized">
+              <xsl:with-param name="node" select="country"/>
+              <xsl:with-param name="ascii" select="true()"/>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:variable>
+        <xsl:if test="$ascii and $ascii-country=''">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">Postal address for '<xsl:call-template name="author-name-for-diags"/>' is incomplete because country information is missing.</xsl:with-param>
+            <xsl:with-param name="inline" select="'no'"/>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:variable name="format">
+          <xsl:if test="/rfc/@version >= 3">
+            <xsl:call-template name="get-country-format">
+              <xsl:with-param name="country" select="$ascii-country"/>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:variable>
+        <xsl:if test="$ascii and contains($format,'%C') and street and not(city)">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">Postal address for '<xsl:call-template name="author-name-for-diags"/>' likely incomplete: street specified, but city is not.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:variable name="postprefix">
+          <xsl:call-template name="get-country-postprefix">
+            <xsl:with-param name="country" select="$ascii-country"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:choose>
+          <!-- A STREETADDRESS, C CITY, D CITYAREA (DISTRICT?), Z (ZIP)CODE  -->
+          <xsl:when test="$format='%A%n%C %S %Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city-region-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%Z %C %S'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code-city-region"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%Z %C/%S'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code-city-region"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="cr-delim" select="'/'"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%Z %C%n%S'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%C %Z%n%S'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%Z %C'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code-city"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="prefix" select="$postprefix"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|region|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%C %Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city-code"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="prefix" select="$postprefix"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|region|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%D%n%C%n%S %Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-cityarea"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%D%n%C-%S%n%Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-cityarea"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city-minus-region"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%Z%n%S%C%D%n%A'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-cityarea"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region-comma-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%C%n%A%n%Z'">
+            <xsl:call-template name="emit-postal-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%Z%n%S%n%A'">
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="prefix" select="$postprefix"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%Z%n%C'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="prefix" select="$postprefix"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%C%n%Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%D %C%n%S %Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-cityarea-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="prefix" select="$postprefix"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="city|cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%D%n%C %Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-cityarea"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%D%n%Z %C, %S'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-cityarea"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code-city-region"><xsl:with-param name="ascii" select="$ascii"/><xsl:with-param name="cr-delim" select="', '"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:when test="$format='%A%n%C%n%S%n%Z'">
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-region"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <!-- %A%n%C, %S %Z -->
+            <xsl:call-template name="emit-postal-street"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-city-comma-region-code"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-country"><xsl:with-param name="ascii" select="$ascii"/></xsl:call-template>
+            <xsl:call-template name="emit-postal-warnings"><xsl:with-param name="nodes" select="cityarea|sortingcode"/></xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:if test="*[not(self::postalLine)]">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">It is not allowed to mix postalLine with other elements; these will be ignored.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:for-each select="postalLine">
+          <xsl:call-template name="emit-postal-line">
+            <xsl:with-param name="value">
+              <xsl:call-template name="extract-normalized">
+                <xsl:with-param name="ascii" select="$ascii"/>
+              </xsl:call-template>
+            </xsl:with-param>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+  <xsl:if test="phone">
+    <xsl:variable name="phone">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="phone"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="prefix">Phone</xsl:with-param>
+      <xsl:with-param name="value" select="$phone"/>
+      <xsl:with-param name="link" select="concat('tel:',translate($phone,' ',''))"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="facsimile">
+    <xsl:variable name="facsimile">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="facsimile"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="prefix">Fax</xsl:with-param>
+      <xsl:with-param name="value" select="$facsimile"/>
+      <xsl:with-param name="link" select="concat('fax:',translate($facsimile,' ',''))"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="email">
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="prefix">
+        <xsl:choose>
+          <xsl:when test="$xml2rfc-rfcedstyle='yes'">Email</xsl:when>
+          <xsl:otherwise>EMail</xsl:otherwise>
+        </xsl:choose>
+      </xsl:with-param>
+      <xsl:with-param name="values">
+        <xsl:for-each select="email">
+          <xsl:variable name="e">
+            <xsl:call-template name="extract-email"/>
+          </xsl:variable>
+          <v>
+            <xsl:if test="$xml2rfc-linkmailto!='no'">
+              <xsl:attribute name="href">
+                <xsl:value-of select="concat('mailto:',normalize-space($e))"/>
+              </xsl:attribute>
+            </xsl:if>
+            <xsl:value-of select="normalize-space($e)"/>
+          </v>
+        </xsl:for-each>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:for-each select="uri">
+    <xsl:variable name="uri">
+      <xsl:call-template name="extract-uri"/>
+    </xsl:variable>
+    <xsl:call-template name="emit-postal-line">
+      <xsl:with-param name="prefix">URI</xsl:with-param>
+      <xsl:with-param name="value" select="$uri"/>
+      <xsl:with-param name="link" select="$uri"/>
+      <xsl:with-param name="annotation" select="@x:annotation"/>
+    </xsl:call-template>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="emit-author">
+  <xsl:param name="ascii" select="true()"/>
+  <b>
+    <xsl:choose>
+      <xsl:when test="(not(@fullname) or @fullname='') and @surname!=''">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">fullname attribute should be specified for author (using surname instead)</xsl:with-param>
+        </xsl:call-template>
+        <xsl:call-template name="format-initials"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="@surname"/>
+      </xsl:when>
+      <xsl:when test="@asciiFullname!='' and $ascii">
+        <xsl:value-of select="@asciiFullname" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@fullname" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </b>
+  <xsl:if test="not(self::contact) and @role">
+    <xsl:text> (</xsl:text>
+    <xsl:value-of select="@role" />
+    <xsl:text>)</xsl:text>
+  </xsl:if>
+  <!-- annotation support for Martin "uuml" Duerst -->
+  <xsl:if test="@x:annotation">
+    <xsl:text> </xsl:text>
+    <i><xsl:value-of select="@x:annotation"/></i>
+  </xsl:if>
+
+  <xsl:if test="normalize-space(concat(organization,organization/@ascii)) != ''">
+    <br/>
+    <xsl:choose>
+      <xsl:when test="organization/@ascii!='' and $ascii">
+        <xsl:value-of select="organization/@ascii" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="organization" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+  
+  <xsl:call-template name="emit-author-details">
+    <xsl:with-param name="ascii" select="$ascii"/>
+  </xsl:call-template>  
+</xsl:template>
+
+<!-- this is a named template because <back> may be absent -->
+<xsl:template name="back">
+  <xsl:for-each select="back">
+    <xsl:call-template name="check-no-text-content"/>
+  </xsl:for-each>
+
+  <!-- add editorial comments -->
+  <xsl:if test="//cref[not(@display) or display!='false'] and $xml2rfc-comments='yes' and $xml2rfc-inline!='yes'">
+    <xsl:call-template name="insertComments" />
+  </xsl:if>
+
+  <!-- next, add information about the document's authors -->
+  <xsl:if test="$xml2rfc-ext-authors-section='before-appendices'">
+    <xsl:call-template name="insertAuthors" />
+  </xsl:if>
+
+  <!-- add all other top-level sections under <back> -->
+  <xsl:apply-templates select="back/*[not(self::references) and not(self::ed:replace and .//references)]" />
+
+  <!-- insert the index if index entries exist -->
+  <!-- note it always comes before the authors section -->
+  <xsl:if test="$has-index">
+    <xsl:call-template name="insertIndex" />
+  </xsl:if>
+
+  <!-- Authors section is the absolute last thing, except for copyright stuff -->
+  <xsl:if test="$xml2rfc-ext-authors-section='end'">
+    <xsl:call-template name="insertAuthors" />
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-private=''">
+    <!-- copyright statements -->
+    <xsl:variable name="copyright">
+      <xsl:call-template name="insertCopyright" />
+    </xsl:variable>
+
+    <!-- emit it -->
+    <xsl:choose>
+      <xsl:when test="function-available('exslt:node-set')">
+        <xsl:apply-templates select="exslt:node-set($copyright)" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="$node-set-warning"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="check-absolute-uri">
+  <xsl:variable name="potential-scheme" select="substring-before(@target,':')"/>
+  <xsl:variable name="invalid-scheme-chars" select="translate($potential-scheme,concat($alnum,'+-.'),'')"/>
+  <xsl:if test="$potential-scheme=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">target attribute not an absolute URI: <xsl:value-of select="@target"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="$potential-scheme!='' and $invalid-scheme-chars!=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">target attribute '<xsl:value-of select="@target"/>' contains invalid scheme name '<xsl:value-of select="$potential-scheme"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="eref[*|text()]">
+  <xsl:call-template name="check-absolute-uri"/>
+  <a href="{@target}">
+    <xsl:apply-templates/>
+  </a>
+</xsl:template>
+
+<xsl:template match="eref[not(*|text())]">
+  <xsl:call-template name="check-absolute-uri"/>
+  <xsl:variable name="in-angles" select="(not(/rfc/@version >= 3) and not(@brackets='none')) or @brackets='angle'"/>
+  <xsl:if test="$in-angles"><xsl:text>&lt;</xsl:text></xsl:if>
+  <a href="{@target}"><xsl:value-of select="@target"/></a>
+  <xsl:if test="$in-angles"><xsl:text>&gt;</xsl:text></xsl:if>
+</xsl:template>
+
+<xsl:template match="figure">
+  <xsl:call-template name="check-no-text-content"/>
+  <!-- warn about the attributes that we do not support -->
+  <xsl:for-each select="@*[local-name()!='title' and local-name()!='suppress-title' and local-name()!='anchor' and local-name()!='pn' and normalize-space(.)!='']">
+    <xsl:if test="local-name(.)!='align' or normalize-space(.)!='left'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('unsupported attribute ',local-name(.),' on figure element')"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:for-each>
+  <xsl:variable name="anch-container">
+    <xsl:choose>
+      <xsl:when test="ancestor::t">span</xsl:when>
+      <xsl:otherwise>div</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:if test="@anchor!=''">
+    <xsl:call-template name="check-anchor"/>
+    <xsl:element name="{$anch-container}">
+      <xsl:attribute name="id"><xsl:value-of select="@anchor"/></xsl:attribute>
+    </xsl:element>
+  </xsl:if>
+  <xsl:variable name="anch">
+    <xsl:call-template name="get-figure-anchor"/>
+  </xsl:variable>
+  <xsl:element name="{$anch-container}">
+    <xsl:attribute name="id"><xsl:value-of select="$anch"/></xsl:attribute>
+    <xsl:apply-templates select="*[not(self::name)]"/>
+  </xsl:element>
+  <xsl:if test="(@title!='' or name) or (@anchor!='' and not(@suppress-title='true'))">
+    <xsl:variable name="n"><xsl:call-template name="get-figure-number"/></xsl:variable>
+    <p class="figure">
+      <xsl:if test="not(starts-with($n,'u'))">
+        <xsl:text>Figure </xsl:text>
+        <xsl:value-of select="$n"/>
+        <xsl:if test="@title!='' or name">: </xsl:if>
+      </xsl:if>
+      <xsl:call-template name="insertTitle"/>
+    </p>
+  </xsl:if>
+</xsl:template>
+
+<xsl:variable name="all-notes" select="/rfc/front/note"/>
+<xsl:variable name="all-edited-notes" select="/rfc/front/ed:replace[.//note]"/>
+
+<!-- TODO:extend for other streams -->
+<xsl:variable name="stream-note-titles">[IESG Note][IESG Note:]</xsl:variable>
+
+<xsl:variable name="notes-not-in-boilerplate" select="$all-notes[not(contains($stream-note-titles,concat('[',normalize-space(@title),']'))) or $xml2rfc-private!='' or $notes-follow-abstract]"/>
+<xsl:variable name="edited-notes-not-in-boilerplate" select="$all-edited-notes[not(contains($stream-note-titles,concat('[',normalize-space(.//note/@title),']'))) or $xml2rfc-private!='' or $notes-follow-abstract]"/>
+<xsl:variable name="notes-in-boilerplate" select="$all-notes[not(not(contains($stream-note-titles,concat('[',normalize-space(@title),']'))) or $xml2rfc-private!='' or $notes-follow-abstract)]"/>
+<xsl:variable name="edited-notes-in-boilerplate" select="$all-edited-notes[not(not(contains($stream-note-titles,concat('[',normalize-space(.//note/@title),']'))) or $xml2rfc-private!='' or $notes-follow-abstract)]"/>
+
+<xsl:template name="draft-sequence-number">
+  <xsl:param name="name"/>
+  <xsl:choose>
+    <xsl:when test="contains($name,'-')">
+      <xsl:call-template name="draft-sequence-number">
+        <xsl:with-param name="name" select="substring-after($name,'-')"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$name"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="draft-base-name">
+  <xsl:param name="name"/>
+  <xsl:variable name="seq">
+    <xsl:call-template name="draft-sequence-number">
+      <xsl:with-param name="name" select="$name"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:value-of select="substring($name,1,string-length($name)-string-length($seq)-1)"/>
+</xsl:template>
+
+<xsl:template name="draft-name-legal">
+  <xsl:param name="name"/>
+
+  <xsl:if test="contains($name,'.')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The Internet-Draft name '<xsl:value-of select="$name"/>' should contain the base name, not the filename (thus no file extension).</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="offending" select="translate($name,concat($lcase,$digits,'-.'),'')"/>
+  <xsl:if test="$offending != ''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The Internet-Draft name  '<xsl:value-of select="$name"/>' should not contain the character '<xsl:value-of select="substring($offending,1,1)"/>'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="contains($name,'--')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The Internet-Draft name '<xsl:value-of select="$name"/>' should not contain the character sequence '--'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="not(starts-with($name,'draft-'))">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The Internet-Draft name '<xsl:value-of select="$name"/>' should start with 'draft-'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="seq">
+    <xsl:call-template name="draft-sequence-number">
+      <xsl:with-param name="name" select="$name"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:if test="$seq='' or ($seq!='latest' and translate($seq,$digits,'')!='')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The Internet-Draft name '<xsl:value-of select="$name"/>' should end with a two-digit sequence number or 'latest'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+    <xsl:if test="string-length($name)-string-length($seq) > 50">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">The Internet-Draft name '<xsl:value-of select="$name"/>', excluding sequence number, should have less than 50 characters.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+
+</xsl:template>
+
+
+<xsl:template match="front">
+  <xsl:call-template name="check-no-text-content"/>
+  <header>
+    <xsl:if test="$xml2rfc-topblock!='no'">
+      <!-- collect information for left column -->
+      <xsl:variable name="leftColumn">
+        <xsl:call-template name="collectLeftHeaderColumn" />
+      </xsl:variable>
+      <!-- collect information for right column -->
+      <xsl:variable name="rightColumn">
+        <xsl:call-template name="collectRightHeaderColumn" />
+      </xsl:variable>
+      <!-- insert the collected information -->
+      <table class="{$css-header}" id="{$anchor-pref}headerblock">
+        <xsl:choose>
+          <xsl:when test="function-available('exslt:node-set')">
+            <xsl:call-template name="emitheader">
+              <xsl:with-param name="lc" select="exslt:node-set($leftColumn)" />
+              <xsl:with-param name="rc" select="exslt:node-set($rightColumn)" />
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="$node-set-warning"/>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </table>
+    </xsl:if>
+
+    <div id="{$anchor-pref}title">
+      <!-- main title -->
+      <h1><xsl:apply-templates select="title"/></h1>
+      <xsl:if test="/rfc/@docName">
+        <xsl:variable name="docname" select="/rfc/@docName"/>
+        <xsl:choose>
+          <xsl:when test="$rfcno!=''">
+            <xsl:call-template name="info">
+              <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' is ignored because an RFC number (<xsl:value-of select="$rfcno"/>) is specified as well.</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <div class="filename">
+              <xsl:variable name="seq">
+                <xsl:call-template name="draft-sequence-number">
+                  <xsl:with-param name="name" select="$docname"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <xsl:variable name="base">
+                <xsl:call-template name="draft-base-name">
+                  <xsl:with-param name="name" select="$docname"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <xsl:variable name="status-uri">
+                <xsl:call-template name="compute-draft-status-uri">
+                  <xsl:with-param name="draftname" select="$base"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <xsl:choose>
+                <xsl:when test="number($seq)>=0">
+                  <xsl:variable name="draft-uri">
+                    <xsl:call-template name="compute-internet-draft-uri">
+                      <xsl:with-param name="internet-draft" select="$docname"/>
+                    </xsl:call-template>
+                  </xsl:variable>
+                  <a href="{$status-uri}" class="smpl"><xsl:value-of select="$base"/></a>
+                  <xsl:text>-</xsl:text>
+                  <a href="{$draft-uri}" class="smpl"><xsl:value-of select="$seq"/></a>
+                </xsl:when>
+                <xsl:when test="$base!=''">
+                  <a href="{$status-uri}" class="smpl"><xsl:value-of select="$docname"/></a>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="$docname"/>
+                </xsl:otherwise>
+              </xsl:choose>
+            </div>
+          </xsl:otherwise>
+        </xsl:choose>
+        
+        <xsl:variable name="si" select="/rfc/front/seriesInfo[@name='Internet-Draft']"/>
+        <xsl:if test="$si and $si/@value!=$docname">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">Inconsistent draft names in /rfc/@docName ('<xsl:value-of select="$docname"/>') and /rfc/seriesInfo ('<xsl:value-of select="$si/@value"/>').</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+
+        <xsl:call-template name="draft-name-legal">
+          <xsl:with-param name="name" select="$docname"/>
+        </xsl:call-template>
+      </xsl:if>
+    </div>
+  </header>
+
+  <!-- insert notice about update -->
+  <xsl:if test="$published-as-rfc">
+    <p class="{$css-publishedasrfc}">
+      <b>Note:</b> a later version of this document has been published as <a href="{$published-as-rfc/@href}"><xsl:value-of select="$published-as-rfc/@title"/></a>.
+    </p>
+  </xsl:if>
+
+  <!-- check for conforming ipr attribute -->
+  <xsl:choose>
+    <xsl:when test="not(/rfc/@ipr)">
+      <xsl:if test="not($is-rfc) and $xml2rfc-private=''">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Either /rfc/@ipr or /rfc/@number is required</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="/rfc/@ipr = 'full2026'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativeWorks'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativeWorksNow'" />
+    <xsl:when test="/rfc/@ipr = 'none'" />
+    <xsl:when test="/rfc/@ipr = 'full3667'" />
+    <xsl:when test="/rfc/@ipr = 'noModification3667'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivatives3667'" />
+    <xsl:when test="/rfc/@ipr = 'full3978'" />
+    <xsl:when test="/rfc/@ipr = 'noModification3978'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivatives3978'" />
+    <xsl:when test="/rfc/@ipr = 'trust200811'" />
+    <xsl:when test="/rfc/@ipr = 'noModificationTrust200811'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativesTrust200811'" />
+    <xsl:when test="/rfc/@ipr = 'trust200902'" />
+    <xsl:when test="/rfc/@ipr = 'noModificationTrust200902'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativesTrust200902'" />
+    <xsl:when test="/rfc/@ipr = 'pre5378Trust200902'" />
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unknown value for /rfc/@ipr: ', /rfc/@ipr)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <xsl:call-template name="insert-errata">
+    <xsl:with-param name="section" select="'boilerplate'"/>
+  </xsl:call-template>
+
+  <xsl:if test="not($abstract-first)">
+    <xsl:if test="$xml2rfc-private=''">
+      <xsl:call-template name="emit-ietf-preamble">
+        <xsl:with-param name="notes" select="$notes-in-boilerplate|$edited-notes-in-boilerplate"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <xsl:apply-templates select="abstract"/>
+
+  <xsl:if test="$notes-follow-abstract">
+    <xsl:apply-templates select="$notes-not-in-boilerplate|$edited-notes-not-in-boilerplate" />
+  </xsl:if>
+
+  <xsl:if test="$abstract-first">
+    <xsl:if test="$xml2rfc-private=''">
+      <xsl:call-template name="emit-ietf-preamble">
+        <xsl:with-param name="notes" select="$notes-in-boilerplate|$edited-notes-in-boilerplate"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <xsl:if test="not($notes-follow-abstract)">
+    <xsl:apply-templates select="$notes-not-in-boilerplate|$edited-notes-not-in-boilerplate" />
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-toc='yes'">
+    <xsl:apply-templates select="/" mode="toc" />
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="string-diff">
+  <xsl:param name="s1"/>
+  <xsl:param name="s2"/>
+  <xsl:param name="p"/>
+
+  <xsl:choose>
+    <xsl:when test="$s1='' and $s2=''"><!-- done --></xsl:when>
+    <xsl:when test="$s1=''">
+      <xsl:value-of select="concat('Extra characters at the end of 1st string: ',$s1)"/>
+    </xsl:when>
+    <xsl:when test="$s2=''">
+      <xsl:value-of select="concat('Extra characters at the end of 2sn string: ',$s2)"/>
+    </xsl:when>
+    <xsl:when test="substring($s1,1,1)!=substring($s2,1,1)">
+      <xsl:value-of select="concat('Strings differ at position ',string-length($p),', 1st string ends in: [[[',$s1,']]], 2nd string ends in: [[[',$s2,']]]')"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="string-diff">
+        <xsl:with-param name="s1" select="substring($s1,2)"/>
+        <xsl:with-param name="s2" select="substring($s2,2)"/>
+        <xsl:with-param name="p" select="concat($p,substring($s1,1,1))"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="emit-ietf-preamble">
+  <xsl:param name="notes"/>
+
+  <!-- Get status info formatted as per RFC2629-->
+  <xsl:variable name="preamble">
+    <xsl:for-each select="/rfc">
+      <xsl:call-template name="insertPreamble">
+        <xsl:with-param name="notes" select="$notes"/>
+      </xsl:call-template>
+    </xsl:for-each>
+  </xsl:variable>
+
+  <!-- emit it -->
+  <xsl:choose>
+    <xsl:when test="function-available('exslt:node-set')">
+      <!-- get document-supplied boilerplate -->
+      <xsl:variable name="userboiler">
+        <xsl:apply-templates select="$src//rfc/front/boilerplate"/>
+      </xsl:variable>
+      <xsl:variable name="generated">
+         <xsl:apply-templates select="exslt:node-set($preamble)"/>
+      </xsl:variable>
+      <xsl:copy-of select="$generated"/>
+      <!--<xsl:message>1: [[[<xsl:value-of select="normalize-space(string($userboiler))"/>]]]</xsl:message>
+      <xsl:message>2: [[[<xsl:value-of select="normalize-space(string($generated))"/>]]]</xsl:message>-->
+      <xsl:variable name="differ" select="$src//rfc/front/boilerplate and normalize-space(string($userboiler))!=normalize-space(string($generated))"/>
+      <xsl:if test="$differ">
+        <xsl:variable name="diff">
+          <xsl:call-template name="string-diff">
+            <xsl:with-param name="s1" select="normalize-space(string($userboiler))"/>
+            <xsl:with-param name="s2" select="normalize-space(string($generated))"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="concat('User-supplied boilerplate differs from auto-generated boilerplate (inserting auto-generated); ', $diff)"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="$node-set-warning"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="iref">
+  <xsl:variable name="anchor"><xsl:call-template name="compute-iref-anchor"/></xsl:variable>
+  <xsl:choose>
+    <xsl:when test="parent::figure">
+      <div id="{$anchor}"/>
+    </xsl:when>
+    <xsl:when test="ancestor::t or ancestor::artwork or ancestor::sourcecode or ancestor::preamble or ancestor::postamble">
+      <span id="{$anchor}"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <div id="{$anchor}"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="compute-iref-anchor">
+  <xsl:variable name="first" select="translate(substring(@item,1,1),$ucase,$lcase)"/>
+  <xsl:variable name="nkey" select="translate($first,$alnum,'')"/>
+  <xsl:choose>
+    <xsl:when test="count(.|$section-level-irefs)=count($section-level-irefs)">
+      <xsl:for-each select="..">
+        <xsl:value-of select="$anchor-pref"/>section.<xsl:call-template name="get-section-number"/>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="$nkey=''">
+      <xsl:value-of select="$anchor-pref"/>iref.<xsl:value-of select="$first"/>.<xsl:number level="any" count="iref[starts-with(translate(@item,$ucase,$lcase),$first) and count(.|$section-level-irefs)!=count($section-level-irefs)]"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$anchor-pref"/>iref.<xsl:number level="any" count="iref[translate(substring(@item,1,1),$alnum,'')!='' and count(.|$section-level-irefs)!=count($section-level-irefs)]"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="compute-extref-anchor">
+  <xsl:variable name="first" select="translate(substring(.,1,1),$ucase,$lcase)"/>
+  <xsl:variable name="nkey" select="translate($first,$lcase,'')"/>
+  <xsl:choose>
+    <xsl:when test="$nkey=''">
+      <xsl:value-of select="$anchor-pref"/>extref.<xsl:value-of select="$first"/>.<xsl:number level="any" count="x:ref[starts-with(translate(.,$ucase,$lcase),$first)]"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$anchor-pref"/>extref.<xsl:number level="any" count="x:ref[translate(substring(.,1,1),concat($lcase,$ucase),'')='']"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- list templates depend on the list style -->
+
+<xsl:template name="list-empty">
+  <ul class="empty">
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ul>
+</xsl:template>
+
+<xsl:template name="list-format">
+  <dl>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </dl>
+</xsl:template>
+
+<xsl:template name="list-hanging">
+  <xsl:variable name="compact">
+    <xsl:call-template name="get-compact-setting"/>
+  </xsl:variable>
+  <!-- insert a hard space for nested lists so that indentation works ok -->
+  <xsl:if test="ancestor::list and normalize-space(preceding-sibling::text())=''">
+    <xsl:text>&#160;</xsl:text>
+  </xsl:if>
+  <dl>
+    <xsl:if test="$compact='yes'">
+      <xsl:attribute name="class">compact</xsl:attribute>
+    </xsl:if>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </dl>
+</xsl:template>
+
+<xsl:template name="list-numbers">
+  <ol>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ol>
+</xsl:template>
+
+<xsl:template name="check-no-hangindent">
+  <xsl:if test="@hangIndent">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg" select="'hangIndent attribute not supported for this list style'"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="list-letters">
+  <xsl:variable name="type">
+    <xsl:choose>
+      <!-- lowercase for even-numbered nesting levels -->
+      <xsl:when test="0=(count(ancestor::list[@style='letters']) mod 2)">a</xsl:when>
+      <!-- uppercase otherwise -->
+      <xsl:otherwise>A</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <ol type="{$type}">
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ol>
+</xsl:template>
+
+<xsl:template name="list-symbols">
+  <ul>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ul>
+</xsl:template>
+
+<xsl:template match="list">
+  <xsl:variable name="style" select="ancestor-or-self::list[@style][1]/@style"/>
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:choose>
+    <xsl:when test="not($style) or $style='empty'">
+      <xsl:call-template name="check-no-hangindent"/>
+      <xsl:call-template name="list-empty"/>
+    </xsl:when>
+    <xsl:when test="starts-with($style, 'format ')">
+      <xsl:call-template name="list-format"/>
+    </xsl:when>
+    <xsl:when test="$style='hanging'">
+      <xsl:call-template name="list-hanging"/>
+    </xsl:when>
+    <xsl:when test="$style='letters'">
+      <xsl:call-template name="check-no-hangindent"/>
+      <xsl:call-template name="list-letters"/>
+    </xsl:when>
+    <xsl:when test="$style='numbers'">
+      <xsl:call-template name="check-no-hangindent"/>
+      <xsl:call-template name="list-numbers"/>
+    </xsl:when>
+    <xsl:when test="$style='symbols'">
+      <xsl:call-template name="check-no-hangindent"/>
+      <xsl:call-template name="list-symbols"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported style attribute: ', $style)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+
+
+<!-- v3 lists -->
+
+<xsl:template match="ol[string-length(@type)>1]">
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="@group">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="@start">
+        <xsl:value-of select="@start"/>
+      </xsl:when>
+      <xsl:otherwise>1</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <div>
+    <xsl:call-template name="attach-paragraph-number-as-id"/>
+    <dl>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:for-each select="li">
+        <xsl:variable name="label">
+          <xsl:call-template name="expand-format-percent">
+            <xsl:with-param name="format" select="../@type"/>
+            <xsl:with-param name="pos" select="$start - 1 + position()"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <dt>
+          <xsl:call-template name="copy-anchor"/>
+          <xsl:value-of select="$label"/>
+        </dt>
+        <dd>
+          <xsl:apply-templates/>
+        </dd>
+      </xsl:for-each>
+    </dl>
+  </div>
+</xsl:template>
+
+<xsl:template match="dl">
+  <xsl:variable name="newl" select="@newline"/>
+  <xsl:variable name="spac" select="@spacing"/>
+  <xsl:variable name="class">
+    <xsl:if test="$spac='compact'">compact </xsl:if>
+    <xsl:if test="$newl='true'">nohang </xsl:if>
+  </xsl:variable>
+  <div>
+    <xsl:if test="not(ancestor::list)">
+      <xsl:call-template name="attach-paragraph-number-as-id"/>
+    </xsl:if>
+    <dl>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:if test="normalize-space($class)!=''">
+        <xsl:attribute name="class"><xsl:value-of select="normalize-space($class)"/></xsl:attribute>
+      </xsl:if>
+      <xsl:for-each select="dt">
+        <xsl:apply-templates select="."/>
+        <xsl:apply-templates select="following-sibling::dd[1]"/>
+      </xsl:for-each>
+    </dl>
+  </div>
+</xsl:template>
+
+<xsl:template match="dt">
+  <dt>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </dt>
+</xsl:template>
+
+<xsl:template match="dd">
+  <dd>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:variable name="indent" select="../@indent"/>
+    <xsl:if test="number($indent)=$indent">
+      <xsl:attribute name="style">margin-left: <xsl:value-of select="$indent div 2"/>em</xsl:attribute>
+    </xsl:if>
+    <xsl:variable name="block-level-children" select="artwork|aside|dl|figure|ol|sourcecode|t|table|ul"/>
+    <xsl:choose>
+      <xsl:when test="$block-level-children">
+        <!-- TODO: improve error handling-->
+        <xsl:for-each select="$block-level-children">
+          <xsl:choose>
+            <xsl:when test="self::t">
+              <p>
+                <xsl:call-template name="copy-anchor"/>
+                <xsl:apply-templates/>
+              </p>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:apply-templates select="."/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <!-- add one nbsp for empty dd elements -->
+    <xsl:if test="normalize-space(.)=''">&#160;</xsl:if>
+  </dd>
+</xsl:template>
+
+<!-- get value of "compact" mode, checking subcompact first, then compact -->
+<xsl:template name="get-compact-setting">
+  <xsl:variable name="t1">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="preceding::processing-instruction('rfc')"/>
+      <xsl:with-param name="attr" select="'subcompact'"/>
+      <xsl:with-param name="default" select="'?'"/>
+      <xsl:with-param name="duplicate-warning" select="'no'"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$t1='?'">
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="preceding::processing-instruction('rfc')"/>
+        <xsl:with-param name="attr" select="'compact'"/>
+        <xsl:with-param name="default" select="'?'"/>
+        <xsl:with-param name="duplicate-warning" select="'no'"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$t1"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="ol-start">
+  <xsl:param name="node"/>
+  <xsl:variable name="group" select="$node/@group"/>
+  <xsl:variable name="prec" select="$node/preceding::ol[@group=$group]"/>
+  <xsl:choose>
+    <xsl:when test="$node/@start">
+      <xsl:value-of select="$node/@start"/>
+    </xsl:when>
+    <xsl:when test="$prec">
+      <xsl:variable name="s">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="$prec[last()]"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:value-of select="$s + count($prec[last()]/li)"/>
+    </xsl:when>
+    <xsl:otherwise>1</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="ol[not(@type) or string-length(@type)=1]">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="@group">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="@start">
+        <xsl:value-of select="@start"/>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <div>
+    <xsl:if test="not(ancestor::list)">
+      <xsl:call-template name="attach-paragraph-number-as-id"/>
+    </xsl:if>
+    <ol>
+      <xsl:if test="$start!=''">
+        <xsl:attribute name="start"><xsl:value-of select="$start"/></xsl:attribute>
+      </xsl:if>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:copy-of select="@type"/>
+      <xsl:apply-templates />
+    </ol>
+  </div> 
+</xsl:template>
+
+<xsl:template match="ul">
+  <div>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:if test="not(ancestor::list)">
+      <xsl:call-template name="attach-paragraph-number-as-id"/>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="not(li) and @x:when-empty">
+        <p>
+          <xsl:call-template name="copy-anchor"/>
+          <xsl:value-of select="@x:when-empty"/>
+        </p>
+      </xsl:when>
+      <xsl:otherwise>
+        <ul>
+          <xsl:call-template name="copy-anchor"/>
+          <xsl:if test="@empty='true'">
+            <xsl:attribute name="class">
+              <xsl:text>empty</xsl:text>
+              <xsl:if test="@bare='true'">
+                <xsl:text> bare</xsl:text>
+                <xsl:call-template name="warning">
+                  <xsl:with-param name="msg">support for "bare" is experimental, see https://trac.tools.ietf.org/tools/xml2rfc/trac/ticket/547 for more information</xsl:with-param>
+                </xsl:call-template>
+              </xsl:if>
+              <xsl:if test="@bare and @bare!='true'">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg">the only valid value for "bare" is "true"</xsl:with-param>
+                </xsl:call-template>
+              </xsl:if>
+            </xsl:attribute>
+          </xsl:if>
+          <xsl:if test="@bare and not(@empty='true')">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">"bare" attribute is ignored when "empty" is not "true"</xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+          <xsl:apply-templates />
+        </ul>
+      </xsl:otherwise>
+    </xsl:choose>
+  </div>
+</xsl:template>
+
+<xsl:template match="li">
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:if test="(parent::ol or parent::ul) and ../@indent and number(../@indent)&gt;4">
+      <xsl:attribute name="style">padding-left: <xsl:value-of select="(../@indent div 2) - 2"/>em</xsl:attribute>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="artset|artwork|blockquote|dl|figure|ol|sourcecode|t|ul">
+        <xsl:choose>
+          <xsl:when test="bcp14|cref|em|eref|iref|relref|strong|sub|sup|tt|xref|text()[normalize-space(.)!='']">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">unexpected content in &lt;li&gt;: can not mix block-level and phrase-level elements</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="bcp14|cref|em|eref|iref|relref|strong|sub|sup|tt|xref|text()[normalize-space(.)!='']">
+        <xsl:choose>
+          <xsl:when test="artset|artwork|blockquote|dl|figure|ol|sourcecode|t|ul">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">unexpected content in &lt;li&gt;: can not mix phrase-level and block-level elements</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">unexpected content in &lt;li&gt;</xsl:with-param>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="not(following-sibling::li)">
+      <xsl:variable name="l">
+        <xsl:for-each select="..">
+          <xsl:call-template name="get-paragraph-number"/>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:if test="xml2rfc-ext-paragraph-links='yes' and $l!=''">
+        <a class='self' href='#{$anchor-pref}section.{$l}'>&#xb6;</a>
+      </xsl:if>
+    </xsl:if>
+  </li>
+</xsl:template>
+
+<!-- same for t(ext) elements -->
+
+<xsl:template name="list-item-generic">
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:for-each select="../..">
+      <xsl:call-template name="insert-issue-pointer"/>
+    </xsl:for-each>
+    <xsl:apply-templates />
+  </li>
+</xsl:template>
+
+<xsl:template name="list-item-hanging">
+  <xsl:if test="@hangText!=''">
+    <dt>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:if test="count(preceding-sibling::t)=0">
+        <xsl:variable name="del-node" select="ancestor::ed:del"/>
+        <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+        <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+        <xsl:for-each select="../..">
+          <xsl:call-template name="insert-issue-pointer">
+            <xsl:with-param name="deleted-anchor" select="$deleted"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:if>
+      <xsl:value-of select="@hangText" />
+    </dt>
+  </xsl:if>
+
+  <xsl:variable name="dd-content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$dd-content!=''">
+      <dd>
+        <xsl:call-template name="insertInsDelClass"/>
+        <!-- if hangIndent present, use 0.7 of the specified value (1em is the width of the "m" character -->
+        <xsl:if test="../@hangIndent">
+          <xsl:attribute name="style">margin-left: <xsl:value-of select="format-number(../@hangIndent * 0.7,'#.#')"/>em</xsl:attribute>
+        </xsl:if>
+        <xsl:apply-templates />
+      </dd>
+    </xsl:when>
+    <xsl:otherwise>
+      <dd>&#160;</dd>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="list-item-format">
+  <xsl:variable name="list" select=".." />
+  <xsl:variable name="format" select="substring-after(../@style,'format ')"/>
+  <xsl:variable name="pos">
+    <xsl:choose>
+      <xsl:when test="$list/@counter">
+        <xsl:number level="any" count="list[@counter=$list/@counter]/t"/>
+      </xsl:when>
+      <xsl:otherwise><xsl:value-of select="1 + count(preceding-sibling::t)"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <dt>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="expand-format-percent">
+      <xsl:with-param name="format" select="$format"/>
+      <xsl:with-param name="pos" select="$pos"/>
+    </xsl:call-template>
+  </dt>
+  <dd>
+    <xsl:apply-templates/>
+  </dd>
+</xsl:template>
+
+<xsl:template match="list/t | list/ed:replace/ed:*/t">
+  <xsl:variable name="style" select="ancestor::list[@style][1]/@style"/>
+  <xsl:choose>
+    <xsl:when test="not($style) or $style='empty' or $style='letters' or $style='numbers' or $style='symbols'">
+      <xsl:if test="@hangText">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="'t/@hangText used on unstyled list'"/>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="list-item-generic"/>
+    </xsl:when>
+    <xsl:when test="starts-with($style, 'format ')">
+      <xsl:call-template name="list-item-format"/>
+    </xsl:when>
+    <xsl:when test="$style='hanging'">
+      <xsl:call-template name="list-item-hanging"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported style attribute: ', $style)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="list-lt-generic">
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="t" />
+  </li>
+</xsl:template>
+
+<xsl:template name="list-lt-format">
+  <xsl:variable name="list" select=".." />
+  <xsl:variable name="format" select="substring-after(../@style,'format ')" />
+  <xsl:variable name="pos">
+    <xsl:choose>
+      <xsl:when test="$list/@counter">
+        <xsl:number level="any" count="list[@counter=$list/@counter]/*" />
+      </xsl:when>
+      <xsl:otherwise><xsl:value-of select="position()"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <dt>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="expand-format-percent">
+      <xsl:with-param name="format" select="$format"/>
+      <xsl:with-param name="pos" select="$pos"/>
+    </xsl:call-template>
+  </dt>
+  <dd>
+    <xsl:apply-templates select="t" />
+  </dd>
+</xsl:template>
+
+<xsl:template name="list-lt-hanging">
+  <xsl:if test="@hangText!=''">
+    <dt>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:variable name="del-node" select="ancestor::ed:del"/>
+      <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+      <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+      <xsl:for-each select="../..">
+        <xsl:call-template name="insert-issue-pointer">
+          <xsl:with-param name="deleted-anchor" select="$deleted"/>
+        </xsl:call-template>
+      </xsl:for-each>
+      <xsl:value-of select="@hangText" />
+    </dt>
+  </xsl:if>
+  <dd>
+    <xsl:call-template name="insertInsDelClass"/>
+    <!-- if hangIndent present, use 0.7 of the specified value (1em is the width of the "m" character -->
+    <xsl:if test="../@hangIndent">
+      <xsl:attribute name="style">margin-left: <xsl:value-of select="format-number(../@hangIndent * 0.7,'#.#')"/>em</xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates select="t" />
+  </dd>
+</xsl:template>
+
+<xsl:template match="list/x:lt">
+  <xsl:variable name="style" select="ancestor::list[@style][1]/@style"/>
+  <xsl:choose>
+    <xsl:when test="$style='letters' or $style='numbers' or $style='symbols'">
+      <xsl:call-template name="list-lt-generic"/>
+    </xsl:when>    
+    <xsl:when test="starts-with($style, 'format ')">
+      <xsl:call-template name="list-lt-format"/>
+    </xsl:when>    
+    <xsl:when test="$style='hanging'">
+      <xsl:call-template name="list-lt-hanging"/>
+    </xsl:when>    
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported style attribute: ', $style)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="expand-format-percent">
+  <xsl:param name="format"/>
+  <xsl:param name="pos"/>
+  
+  <xsl:choose>
+    <xsl:when test="$format=''"><!-- done--></xsl:when>
+    <xsl:when test="substring($format,1,1)!='%' or string-length($format)=1">
+      <xsl:value-of select="substring($format,1,1)"/>
+      <xsl:call-template name="expand-format-percent">
+        <xsl:with-param name="format" select="substring($format,2)"/>
+        <xsl:with-param name="pos" select="$pos"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="f" select="substring($format,2,1)"/>
+      <xsl:choose>
+        <xsl:when test="$f='%'">%</xsl:when>
+        <xsl:when test="$f='c'"><xsl:number value="$pos" format="a"/></xsl:when>
+        <xsl:when test="$f='C'"><xsl:number value="$pos" format="A"/></xsl:when>
+        <xsl:when test="$f='d'"><xsl:number value="$pos"/></xsl:when>
+        <xsl:when test="$f='i'"><xsl:number value="$pos" format="i"/></xsl:when>
+        <xsl:when test="$f='I'"><xsl:number value="$pos" format="I"/></xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('Unsupported % format: ', $f)"/>
+            <xsl:with-param name="inline" select="'no'"/>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:call-template name="expand-format-percent">
+        <xsl:with-param name="format" select="substring($format,3)"/>
+        <xsl:with-param name="pos" select="$pos"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+  
+</xsl:template>
+
+<xsl:template match="middle">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:apply-templates />
+  <xsl:apply-templates select="../back//references"/>
+</xsl:template>
+
+<xsl:template match="note">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:variable name="classes">
+    <xsl:text>note</xsl:text>
+    <xsl:text> </xsl:text>
+    <xsl:if test="@removeInRFC='true'">rfcEditorRemove</xsl:if>
+  </xsl:variable>
+  <xsl:variable name="num"><xsl:number/></xsl:variable>
+  <section class="{normalize-space($classes)}">
+    <xsl:call-template name="copy-anchor"/>
+    <h2 id="{$anchor-pref}note.{$num}" >
+      <xsl:call-template name="insertInsDelClass"/>
+      <a href="#{$anchor-pref}note.{$num}">
+        <xsl:call-template name="insertTitle" />
+      </a>
+    </h2>
+    <xsl:if test="@removeInRFC='true' and (not(t) or t[1]!=$note-removeInRFC)">
+      <xsl:variable name="t">
+        <t><xsl:value-of select="$note-removeInRFC"/></t>
+      </xsl:variable>
+      <xsl:variable name="link" select="concat($anchor-pref,'note.',$num,'.p.1')"/>
+      <div id="{$link}">
+        <xsl:apply-templates mode="t-content" select="exslt:node-set($t)//text()">
+          <xsl:with-param name="inherited-self-link" select="$link"/>
+        </xsl:apply-templates>
+      </div>
+    </xsl:if>
+    <xsl:apply-templates />
+  </section>
+</xsl:template>
+
+<xsl:template match="postamble">
+  <xsl:if test="normalize-space(.) != ''">
+    <p>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:call-template name="editingMark" />
+      <xsl:apply-templates />
+    </p>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="preamble">
+  <xsl:if test="normalize-space(.) != ''">
+    <p>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:call-template name="editingMark" />
+      <xsl:apply-templates />
+    </p>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="computed-auto-target">
+  <xsl:param name="bib" select="."/>
+  <xsl:param name="ref"/>
+
+  <xsl:variable name="sec">
+    <xsl:choose>
+      <xsl:when test="$ref and starts-with($ref/@x:rel,'#') and not($ref/@x:sec) and not($ref/@section)">
+        <xsl:variable name="extdoc" select="document($bib/x:source/@href)"/>
+        <xsl:variable name="anch" select="substring-after($ref/@x:rel,'#')"/>
+        <xsl:for-each select="$extdoc//*[@anchor=$anch or x:anchor-alias/@value=$anch]">
+          <xsl:call-template name="get-section-number"/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:when test="$ref and $ref/@section">
+        <xsl:value-of select="$ref/@section"/>
+      </xsl:when>
+      <xsl:when test="$ref">
+        <xsl:value-of select="$ref/@x:sec"/>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$ref and $bib/x:source/@href and $bib/x:source/@basename and $ref/@x:rel">
+      <xsl:variable name="extdoc" select="document($bib/x:source/@href)"/>
+      <xsl:variable name="targetanchor">
+        <xsl:variable name="anch" select="substring-after($ref/@x:rel,'#')"/>
+        <xsl:value-of select="($extdoc//*[@anchor=$anch or x:anchor-alias/@value=$anch])[1]/@anchor"/>
+      </xsl:variable>
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension,'#',$targetanchor)"/>
+    </xsl:when>
+    <xsl:when test="$ref and $bib/x:source/@href and $bib/x:source/@basename and $sec!=''">
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension,'#')" />
+      <xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$sec"/>
+      <!-- sanity check on target document -->
+      <xsl:variable name="d" select="document($bib/x:source/@href)"/>
+      <xsl:variable name="sections">
+        <xsl:text> </xsl:text>
+        <xsl:apply-templates select="$d//rfc" mode="get-section-numbers"/>
+        <xsl:text> </xsl:text>
+      </xsl:variable>
+      <xsl:if test="not(contains($sections,concat(' ',$sec,' ')))">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('apparently dangling reference to ',$sec,' of ',$bib/@anchor)"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$ref and $bib/x:source/@href and $bib/x:source/@basename and $ref/@anchor">
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension,'#',$ref/@anchor)" />
+    </xsl:when>
+    <!-- tools.ietf.org won't have the "-latest" draft -->
+    <xsl:when test="$bib/x:source/@href and $bib/x:source/@basename and substring($bib/x:source/@basename, (string-length($bib/x:source/@basename) - string-length('-latest')) + 1)='-latest'">
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension)" />
+    </xsl:when>
+    <!-- TODO: this should handle the case where there's one BCP entry but
+    multiple RFC entries in a more useful way-->
+    <xsl:when test="$bib//seriesInfo/@name='RFC'">
+      <xsl:variable name="rfcEntries" select="$bib//seriesInfo[@name='RFC']"/>
+      <xsl:if test="count($rfcEntries)!=1">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('seriesInfo/@name=RFC encountered multiple times for reference ',$bib/@anchor,', will generate link to first entry only')"/>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="compute-rfc-uri">
+        <xsl:with-param name="rfc" select="$rfcEntries[1]/@value"/>
+      </xsl:call-template>
+      <xsl:if test="$ref and $sec!='' and $rfcUrlFragSection and $rfcUrlFragAppendix">
+        <xsl:choose>
+          <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+            <xsl:value-of select="concat('#',$rfcUrlFragAppendix,$sec)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="concat('#',$rfcUrlFragSection,$sec)"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$bib//seriesInfo/@name='Internet-Draft'">
+      <xsl:variable name="draftName" select="$bib//seriesInfo[@name='Internet-Draft']/@value"/>
+      <xsl:variable name="endsWithLatest" select="substring($draftName, string-length($draftName) - string-length('-latest') + 1) = '-latest'"/>
+      <xsl:if test="not($endsWithLatest)">
+        <xsl:call-template name="compute-internet-draft-uri">
+          <xsl:with-param name="internet-draft" select="$draftName"/>
+          <xsl:with-param name="ref" select="$bib"/>
+        </xsl:call-template>
+        <xsl:if test="$ref and $sec!='' and $internetDraftUrlFragSection and $internetDraftUrlFragAppendix">
+          <xsl:choose>
+            <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+              <xsl:value-of select="concat('#',$internetDraftUrlFragAppendix,$sec)"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="concat('#',$internetDraftUrlFragSection,$sec)"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$bib//x:source/@href and document($bib//x:source/@href)/rfc/@number">
+      <xsl:call-template name="compute-rfc-uri">
+        <xsl:with-param name="rfc" select="document($bib//x:source/@href)/rfc/@number"/>
+      </xsl:call-template>
+      <xsl:if test="$ref and $sec!='' and $rfcUrlFragSection and $rfcUrlFragAppendix">
+        <xsl:choose>
+          <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+            <xsl:value-of select="concat('#',$rfcUrlFragAppendix,$sec)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="concat('#',$rfcUrlFragSection,$sec)"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$bib//x:source/@href and document($bib//x:source/@href)/rfc/@docName">
+      <xsl:variable name="draftName" select="document($bib//x:source/@href)/rfc/@docName"/>
+      <xsl:variable name="endsWithLatest" select="substring($draftName, string-length($draftName) - string-length('-latest') + 1) = '-latest'"/>
+      <xsl:if test="not($endsWithLatest)">
+        <xsl:call-template name="compute-internet-draft-uri">
+          <xsl:with-param name="internet-draft" select="$draftName"/>
+          <xsl:with-param name="ref" select="$bib"/>
+        </xsl:call-template>
+        <xsl:if test="$ref and $sec!='' and $internetDraftUrlFragSection and $internetDraftUrlFragAppendix">
+          <xsl:choose>
+            <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+              <xsl:value-of select="concat('#',$internetDraftUrlFragAppendix,$sec)"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="concat('#',$internetDraftUrlFragSection,$sec)"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<!-- generates a string with white-space separated section numbers -->
+<xsl:template match="node()|@*" mode="get-section-numbers">
+  <xsl:apply-templates select="*" mode="get-section-numbers"/>
+</xsl:template>
+<xsl:template match="section|references|appendix" mode="get-section-numbers">
+  <xsl:call-template name="get-section-number"/>
+  <xsl:text> </xsl:text>
+  <xsl:apply-templates select="*" mode="get-section-numbers"/>
+</xsl:template>
+
+<!-- titles as plain text -->
+<xsl:template match="text()" mode="as-string">
+  <xsl:value-of select="."/>
+</xsl:template>
+<xsl:template match="*" mode="as-string">
+  <xsl:apply-templates select="node()" mode="as-string"/>
+</xsl:template>
+<xsl:template match="br" mode="as-string">
+  <xsl:text> </xsl:text>
+</xsl:template>
+
+<xsl:template name="get-title-as-string">
+  <xsl:param name="node" select="."/>
+  <xsl:variable name="t">
+    <xsl:for-each select="$node">
+      <xsl:choose>
+        <xsl:when test="name">
+          <xsl:apply-templates select="name/node()" mode="as-string"/>
+        </xsl:when>
+        <xsl:when test="@title">
+          <xsl:value-of select="@title"/>
+        </xsl:when>
+        <xsl:when test="self::abstract">Abstract</xsl:when>
+        <xsl:when test="self::references">References</xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:value-of select="normalize-space($t)"/>
+</xsl:template>
+
+<xsl:template name="compute-section-number">
+  <xsl:param name="bib"/>
+  <xsl:param name="ref"/>
+  
+  <xsl:variable name="anch" select="substring-after($ref/@x:rel,'#')"/>
+  
+  <xsl:choose>
+    <xsl:when test="$anch=''">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">Not a fragment identifier: <xsl:value-of select="$ref/@x:rel"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="extdoc" select="document($bib/x:source/@href)"/>
+      <xsl:variable name="nodes" select="$extdoc//*[@anchor=$anch or x:anchor-alias/@value=$anch]"/>
+      <xsl:if test="not($nodes)">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Anchor '<xsl:value-of select="$anch"/>' in <xsl:value-of select="$bib/@anchor"/> not found in source file '<xsl:value-of select="$bib/x:source/@href"/>'.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:variable name="number">
+        <xsl:for-each select="$nodes">
+          <xsl:call-template name="get-section-number"/>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="starts-with($number,$unnumbered)">
+          <xsl:choose>
+            <xsl:when test="$nodes[1]/ancestor::back">A@</xsl:when>
+            <xsl:otherwise>S@</xsl:otherwise>
+          </xsl:choose>
+          <xsl:call-template name="get-title-as-string">
+            <xsl:with-param name="node" select="$nodes[1]"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$number"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="computed-target">
+  <xsl:param name="bib"/>
+  <xsl:param name="ref"/>
+
+  <xsl:variable name="bibtarget">
+    <xsl:choose>
+      <xsl:when test="starts-with($bib/@target,'http://www.rfc-editor.org/info/rfc') or starts-with($bib/@target,'https://www.rfc-editor.org/info/rfc') and $ref and ($ref/@x:sec or $ref/@x:rel or $ref/@section or $ref/@relative)">
+        <!--ignored, use tools.ietf.org link instead -->
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$bib/@target"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <xsl:choose>
+    <xsl:when test="$bibtarget!=''">
+      <xsl:if test="$ref and $ref/@x:sec">
+        <xsl:choose>
+          <xsl:when test="$ref/@x:rel">
+            <xsl:value-of select="concat($bib/@target,$ref/@x:rel)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Can't generate section link for to <xsl:value-of select="$bib/@anchor"/>; no @x:rel specified</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      <xsl:if test="$ref and $ref/@section">
+        <xsl:choose>
+          <xsl:when test="$ref/@relative">
+            <xsl:value-of select="concat($bib/@target,$ref/@relative)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Can't generate section link for to <xsl:value-of select="$bib/@anchor"/>; no @relative specified</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="computed-auto-target">
+        <xsl:with-param name="bib" select="$bib"/>
+        <xsl:with-param name="ref" select="$ref"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="compute-doi">
+  <xsl:param name="rfc"/>
+  <xsl:choose>
+    <xsl:when test="$rfc!=''">
+      <xsl:value-of select="concat('10.17487/RFC', format-number($rfc,'#0000'))"/>
+    </xsl:when>
+    <!-- xref seems to be for BCP, not RFC -->
+    <xsl:when test=".//seriesInfo[@name='BCP'] and starts-with(@anchor, 'BCP')" />
+    <xsl:when test=".//seriesInfo[@name='RFC'] and not(normalize-space((.//organization)[1])='RFC Errata') and not(starts-with(@target,'http://www.rfc-editor.org') or starts-with(@target,'https://www.rfc-editor.org'))">
+      <xsl:variable name="t" select=".//seriesInfo[@name='RFC'][1]/@value"/>
+      <xsl:value-of select="concat('10.17487/RFC', format-number($t,'#0000'))"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<!-- processed elsewhere -->
+<xsl:template match="displayreference">
+  <xsl:variable name="t" select="@to"/>
+  <xsl:if test="//reference/@anchor=$t or count(//displayreference[@to=$t])!=1">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg">displayreference <xsl:value-of select="$t"/> will create non-unique reference name.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="displayname-for-author">
+  <xsl:param name="not-reversed"/>
+
+  <xsl:variable name="surname">
+    <xsl:call-template name="get-author-surname"/>
+  </xsl:variable>
+  
+  <xsl:variable name="initials">
+    <xsl:call-template name="format-initials"/>
+  </xsl:variable>
+  <xsl:variable name="truncated-initials">
+    <xsl:call-template name="truncate-initials">
+      <xsl:with-param name="initials" select="$initials"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <!-- surname/initials is reversed for last author except when it's the only one -->
+  <xsl:choose>
+    <xsl:when test="$truncated-initials='' and $surname">
+      <xsl:value-of select="$surname"/>
+    </xsl:when>
+    <xsl:when test="$not-reversed">
+      <xsl:value-of select="concat($truncated-initials,' ',@surname)" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat($surname,', ',$truncated-initials)" />
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:if test="@asciiSurname!='' or @asciiInitials!=''">
+    <xsl:text> (</xsl:text>
+    <xsl:variable name="i">
+      <xsl:choose>
+        <xsl:when test="@asciiInitials!=''">
+          <xsl:value-of select="@asciiInitials"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$truncated-initials"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="s">
+      <xsl:choose>
+        <xsl:when test="@asciiSurname!=''">
+          <xsl:value-of select="@asciiSurname"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$surname"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$i=''">
+        <xsl:value-of select="$s"/>
+      </xsl:when>
+      <xsl:when test="$not-reversed">
+        <xsl:value-of select="concat($i,' ',$s)" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="concat($s,', ',$i)" />
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:text>)</xsl:text>
+  </xsl:if> 
+  <xsl:if test="@role='editor'">
+    <xsl:text>, Ed.</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="link-ref-title-to">
+  <xsl:choose>
+    <xsl:when test="starts-with(@target,'http://www.rfc-editor.org/info/rfc') or starts-with(@target,'https://www.rfc-editor.org/info/rfc')">
+      <xsl:call-template name="info">
+        <xsl:with-param name="msg">Ignoring @target <xsl:value-of select="@target"/> in link calculation</xsl:with-param>
+      </xsl:call-template>
+      <xsl:call-template name="computed-auto-target"/>
+    </xsl:when>
+    <xsl:when test=".//seriesInfo/@name='RFC' and (@target='http://www.rfc-editor.org' or @target='https://www.rfc-editor.org') and starts-with(front/title,'Errata ID ') and front/author/organization='RFC Errata'">
+      <!-- check for erratum link -->
+      <xsl:variable name="eid" select="normalize-space(substring(front/title,string-length('Errata ID ')))"/>
+      <xsl:call-template name="compute-rfc-erratum-uri">
+        <xsl:with-param name="eid" select="$eid"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="@target">
+      <xsl:if test="normalize-space(@target)=''">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">invalid (empty) target attribute in reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:value-of select="normalize-space(@target)" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="computed-auto-target"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="emit-series-info">
+  <xsl:param name="multiple-rfcs" select="false()"/>
+  <xsl:param name="doi"/>
+
+  <xsl:choose>
+    <xsl:when test="not(@name) and not(@value) and ./text()">
+      <xsl:text>, </xsl:text>
+      <xsl:value-of select="."/>
+    </xsl:when>
+    <xsl:when test="@name='RFC' and $multiple-rfcs">
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-rfc-uri">
+          <xsl:with-param name="rfc" select="@value"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:text>, </xsl:text>
+      <xsl:call-template name="emit-link">
+        <xsl:with-param name="target" select="$uri"/>
+        <xsl:with-param name="text">
+          <xsl:value-of select="@name" />
+          <xsl:if test="@value!=''"><xsl:text> </xsl:text><xsl:value-of select="@value" /></xsl:if>
+        </xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="@name='DOI'">
+      <xsl:choose>
+        <xsl:when test="starts-with(@value,'10.17487/RFC') and $xml2rfc-ext-insert-doi='no'">
+          <xsl:call-template name="info">
+            <xsl:with-param name="msg">Removing DOI <xsl:value-of select="@value"/> from &lt;reference> element</xsl:with-param>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:variable name="uri">
+            <xsl:call-template name="compute-doi-uri">
+              <xsl:with-param name="doi" select="@value"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <xsl:text>, </xsl:text>
+          <xsl:call-template name="emit-link">
+            <xsl:with-param name="target" select="$uri"/>
+            <xsl:with-param name="text">
+              <xsl:value-of select="@name" />
+              <xsl:if test="@value!=''"><xsl:text> </xsl:text><xsl:value-of select="@value" /></xsl:if>
+            </xsl:with-param>
+          </xsl:call-template>
+          <xsl:if test="$doi!='' and $doi!=@value">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Unexpected DOI for RFC, found <xsl:value-of select="@value"/>, expected <xsl:value-of select="$doi"/></xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="@name='ISBN'">
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-isbn-uri">
+          <xsl:with-param name="isbn" select="@value"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:text>, </xsl:text>
+      <xsl:call-template name="emit-link">
+        <xsl:with-param name="target" select="$uri"/>
+        <xsl:with-param name="text">
+          <xsl:value-of select="@name" />
+          <xsl:if test="@value!=''"><xsl:text> </xsl:text><xsl:value-of select="@value" /></xsl:if>
+        </xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="@name='Internet-Draft'">
+      <xsl:variable name="basename">
+        <xsl:call-template name="draft-base-name">
+          <xsl:with-param name="name" select="@value"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-draft-status-uri">
+          <xsl:with-param name="draftname" select="$basename"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:text>, </xsl:text>
+      <xsl:choose>
+        <xsl:when test="number($rfcno) > 7375">
+          <!-- special case in RFC formatting since 2015 -->
+          <xsl:call-template name="emit-link">
+            <xsl:with-param name="target" select="$uri"/>
+            <xsl:with-param name="text">Work in Progress</xsl:with-param>
+          </xsl:call-template>
+          <xsl:text>, </xsl:text>
+          <xsl:value-of select="@value" />
+        </xsl:when>
+        <xsl:when test="/rfc/@version >= 3 and $pub-yearmonth >= 201910">
+          <!-- https://tools.ietf.org/html/draft-flanagan-7322bis-04#section-4.8.6.3 -->
+          <xsl:call-template name="emit-link">
+            <xsl:with-param name="target" select="$uri"/>
+            <xsl:with-param name="text">Work in Progress</xsl:with-param>
+          </xsl:call-template>
+          <xsl:text>, Internet-Draft, </xsl:text>
+          <xsl:value-of select="@value" />
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@name" />
+          <xsl:if test="@value!=''"><xsl:text> </xsl:text><xsl:value-of select="@value" /></xsl:if>
+          <xsl:if test="@name='Internet-Draft'">
+            <xsl:text> (</xsl:text>
+              <xsl:call-template name="emit-link">
+                <xsl:with-param name="target" select="$uri"/>
+                <xsl:with-param name="text">work in progress</xsl:with-param>
+              </xsl:call-template>
+            <xsl:text>)</xsl:text>
+          </xsl:if>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>, </xsl:text>
+      <xsl:value-of select="@name"/>
+      <xsl:if test="@value!=''"><xsl:text> </xsl:text><xsl:value-of select="@value"/></xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <!-- check that BCP FYI STD RFC are in the right order -->
+  <xsl:if test="(@name='BCP' or @name='FYI' or @name='STD') and preceding-sibling::seriesInfo[@name='RFC']">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">RFC number preceding <xsl:value-of select="@name"/> number in reference '<xsl:value-of select="../@anchor"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="find-ref-in-artwork">
+  <xsl:variable name="lookup" select="concat('[',@anchor,']')"/>
+  <xsl:variable name="aw" select="//artwork[contains(.,$lookup)]|//sourcecode[contains(.,$lookup)]"/>
+  <xsl:for-each select="$aw[1]">
+    <xsl:text> (but found in </xsl:text>
+    <xsl:value-of select="local-name()"/>
+    <xsl:text> element</xsl:text>
+    <xsl:call-template name="lineno"/>
+    <xsl:text>, consider marking up the text content which is supported by this processor, see https://greenbytes.de/tech/webdav/rfc2629xslt/rfc2629xslt.html#extension.pis)</xsl:text>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="reference">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <!-- check for reference to reference -->
+  <xsl:variable name="anchor" select="@anchor"/>
+  <xsl:choose>
+    <xsl:when test="not(@anchor)">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">missing anchor attribute on reference, containing the text: <xsl:value-of select="normalize-space(.)"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(ancestor::ed:del) and (ancestor::rfc and not(key('xref-item',$anchor)))">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">unused reference '<xsl:value-of select="@anchor"/>'<xsl:call-template name="find-ref-in-artwork"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(ancestor::ed:del) and (not(ancestor::rfc) and not($src//xref[@target=$anchor]))">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">unused (included) reference '<xsl:value-of select="@anchor"/>'<xsl:call-template name="find-ref-in-artwork"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+
+  <!-- check normative/informative -->
+  <xsl:variable name="t-r-is-normative" select="ancestor-or-self::*[@x:nrm][1]"/>
+  <xsl:variable name="r-is-normative" select="$t-r-is-normative/@x:nrm='true'"/>
+  <xsl:if test="$r-is-normative and not(ancestor::ed:del)">
+    <xsl:variable name="tst">
+      <xsl:for-each select="key('xref-item',$anchor)">
+        <xsl:variable name="t-is-normative" select="ancestor-or-self::*[@x:nrm][1]"/>
+        <xsl:variable name="is-normative" select="$t-is-normative/@x:nrm='true'"/>
+        <xsl:if test="$is-normative">OK</xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:if test="$tst=''">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">all references to the normative reference '<xsl:value-of select="@anchor"/>' appear to be informative</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <xsl:call-template name="check-anchor"/>
+
+  <dt id="{@anchor}">
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:variable name="del-node" select="ancestor::ed:del"/>
+    <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+    <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+    <xsl:for-each select="../..">
+      <xsl:call-template name="insert-issue-pointer">
+        <xsl:with-param name="deleted-anchor" select="$deleted"/>
+      </xsl:call-template>
+    </xsl:for-each>
+    <xsl:call-template name="reference-name"/>
+  </dt>
+
+  <xsl:call-template name="insert-reference-body"/>
+
+</xsl:template>
+
+<xsl:template name="insert-reference-body">
+  <xsl:param name="in-reference-group" select="false()"/>
+
+  <xsl:variable name="front" select="front[1]|document(x:source/@href)/rfc/front[1]"/>
+  <xsl:if test="count($front)=0">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg">&lt;front> element missing for '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="count($front)>1">
+    <xsl:call-template name="info">
+      <xsl:with-param name="msg">&lt;front> can be omitted when &lt;x:source> is specified (for '<xsl:value-of select="@anchor"/>')</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  
+  <xsl:variable name="target">
+    <xsl:call-template name="link-ref-title-to"/>
+  </xsl:variable>
+
+  <dd>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:if test="$in-reference-group">
+      <xsl:call-template name="copy-anchor"/>
+    </xsl:if>
+    
+    <xsl:for-each select="$front[1]/author">
+      <xsl:choose>
+        <xsl:when test="@surname!='' or (@fullname!='' and normalize-space(@fullname)!=normalize-space(organization))">
+          <xsl:variable name="displayname">
+            <xsl:call-template name="displayname-for-author">
+              <xsl:with-param name="not-reversed" select="position()=last() and position()!=1"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <xsl:choose>
+            <xsl:when test="address/email and $xml2rfc-linkmailto!='no'">
+              <a href="mailto:{address/email}"><xsl:value-of select="$displayname" /></a>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$displayname" />
+            </xsl:otherwise>
+          </xsl:choose>
+
+          <xsl:choose>
+            <xsl:when test="position()=last() - 1">
+              <xsl:if test="last() &gt; 2">,</xsl:if>
+              <xsl:text> and </xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:text>, </xsl:text>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="organization/text()">
+          <xsl:choose>
+            <xsl:when test="address/uri">
+              <a href="{address/uri}"><xsl:value-of select="organization" /></a>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="organization" />
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:if test="organization/@ascii">
+            <xsl:value-of select="concat(' (',normalize-space(organization/@ascii),')')"/>
+          </xsl:if>
+          <xsl:choose>
+            <xsl:when test="position()=last() - 1">
+              <xsl:if test="last() &gt; 2">,</xsl:if>
+              <xsl:text> and </xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:text>, </xsl:text>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:otherwise />
+      </xsl:choose>
+    </xsl:for-each>
+
+    <xsl:variable name="quoted" select="not($front[1]/title/@x:quotes='false') and not(@quoteTitle='false')"/>
+    <xsl:variable name="title">
+      <xsl:apply-templates select="$front[1]/title/node()" mode="get-text-content"/>
+    </xsl:variable>
+
+    <xsl:if test="$quoted">&#8220;</xsl:if>
+    <xsl:choose>
+      <xsl:when test="string-length($target) &gt; 0">
+        <a href="{$target}"><xsl:value-of select="$title"/></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="$quoted">&#8221;</xsl:if>
+
+    <xsl:if test="$front[1]/title/@ascii!=''">
+      <xsl:text> (</xsl:text>
+      <xsl:if test="$quoted">&#8220;</xsl:if>
+      <xsl:value-of select="normalize-space($front[1]/title/@ascii)" />
+      <xsl:if test="$quoted">&#8221;</xsl:if>
+      <xsl:text>)</xsl:text>
+    </xsl:if> 
+
+    <xsl:variable name="si" select="seriesInfo|$front[1]/seriesInfo"/>
+    <xsl:if test="seriesInfo and $front[1]/seriesInfo">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">seriesInfo present both on reference and reference/front</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    
+    <xsl:variable name="doi">
+      <xsl:choose>
+        <xsl:when test="$si">
+          <xsl:call-template name="compute-doi"/>
+        </xsl:when>
+        <xsl:when test="document(x:source/@href)/rfc/@number">
+          <xsl:call-template name="compute-doi">
+            <xsl:with-param name="rfc" select="document(x:source/@href)/rfc/@number"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:variable>
+
+    <xsl:for-each select="$si">
+      <xsl:call-template name="emit-series-info">
+        <xsl:with-param name="multiple-rfcs" select="count($si[@name='RFC']) > 1"/>
+        <xsl:with-param name="doi" select="$doi"/>
+      </xsl:call-template>
+    </xsl:for-each>
+
+    <!-- fall back to x:source when needed -->
+    <xsl:if test="not($si) and x:source/@href">
+      <xsl:variable name="derivedsi" myns:namespaceless-elements="xml2rfc">
+        <xsl:if test="document(x:source/@href)/rfc/@docName">
+          <seriesInfo name="Internet-Draft" value="{document(x:source/@href)/rfc/@docName}"/>
+        </xsl:if>
+        <xsl:if test="document(x:source/@href)/rfc/@number">
+          <seriesInfo name="RFC" value="{document(x:source/@href)/rfc/@number}"/>
+        </xsl:if>
+      </xsl:variable>
+      <xsl:variable name="tsi" select="exslt:node-set($derivedsi)/seriesInfo"/>
+      <xsl:for-each select="$tsi">
+        <xsl:call-template name="emit-series-info"/>
+      </xsl:for-each>
+    </xsl:if>
+
+    <!-- Insert DOI for RFCs -->
+    <xsl:if test="$xml2rfc-ext-insert-doi='yes' and $doi!='' and not($si[@name='DOI'])">
+      <xsl:text>, </xsl:text>
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-doi-uri">
+          <xsl:with-param name="doi" select="$doi"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <a href="{$uri}">DOI <xsl:value-of select="$doi"/></a>
+    </xsl:if>
+
+    <!-- avoid hacks using seriesInfo when it's not really series information -->
+    <xsl:for-each select="x:prose|refcontent">
+      <xsl:text>, </xsl:text>
+      <xsl:apply-templates/>
+    </xsl:for-each>
+
+    <xsl:call-template name="insert-pub-date">
+      <xsl:with-param name="front" select="$front[1]"/>
+    </xsl:call-template>
+
+    <xsl:choose>
+      <xsl:when test="string-length(normalize-space(@target)) &gt; 0">
+        <!-- hack: suppress specified target in reference group when it appears to be an info link to the RFC editor page -->
+        <xsl:if test="not($in-reference-group) or not(contains(@target,'www.rfc-editor.org/info/rfc'))">
+          <xsl:text>, &lt;</xsl:text>
+          <a href="{normalize-space(@target)}"><xsl:value-of select="normalize-space(@target)"/></a>
+          <xsl:text>&gt;</xsl:text>
+        </xsl:if>
+      </xsl:when>
+      <xsl:when test="not($in-reference-group) and $xml2rfc-ext-link-rfc-to-info-page='yes' and $si[@name='BCP'] and starts-with(@anchor, 'BCP')">
+        <xsl:text>, &lt;</xsl:text>
+        <xsl:variable name="uri">
+          <xsl:call-template name="compute-rfc-info-uri">
+            <xsl:with-param name="type" select="'bcp'"/>
+            <xsl:with-param name="no" select="$si[@name='BCP']/@value"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <a href="{$uri}"><xsl:value-of select="$uri"/></a>
+        <xsl:text>&gt;</xsl:text>
+      </xsl:when>
+      <xsl:when test="not($in-reference-group) and $xml2rfc-ext-link-rfc-to-info-page='yes' and $si[@name='RFC']">
+        <xsl:text>, &lt;</xsl:text>
+        <xsl:variable name="uri">
+          <xsl:call-template name="compute-rfc-info-uri">
+            <xsl:with-param name="type" select="'rfc'"/>
+            <xsl:with-param name="no" select="$si[@name='RFC']/@value"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <a href="{$uri}"><xsl:value-of select="$uri"/></a>
+        <xsl:text>&gt;</xsl:text>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+
+    <xsl:text>.</xsl:text>
+
+    <xsl:for-each select="annotation">
+      <br />
+      <xsl:apply-templates />
+    </xsl:for-each>
+  </dd>
+  
+  <!-- sanity check on x:source/x:has -->
+  <xsl:for-each select="x:source/x:has">
+    <xsl:variable name="doc" select="document(../@href)"/>
+    <xsl:variable name="anch" select="@anchor"/>
+    <xsl:variable name="targ" select="@target"/>
+    <xsl:if test="not(//*[@target=$anch])">
+      <xsl:call-template name="info">
+        <xsl:with-param name="msg">x:has with anchor '<xsl:value-of select="$anch"/>' in <xsl:value-of select="../@href"/> is unused</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="@target">
+        <xsl:if test="not($doc//*[@anchor=$targ]) and not($doc//x:anchor-alias/@value=$targ)">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">x:has with target '<xsl:value-of select="$targ"/>' not defined in <xsl:value-of select="../@href"/></xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:if test="not($doc//*[@anchor=$anch]) and not($doc//x:anchor-alias/@value=$anch)">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">x:has with anchor '<xsl:value-of select="$anch"/>' not defined in <xsl:value-of select="../@href"/></xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="insert-pub-date">
+  <xsl:param name="front"/>
+  
+  <xsl:if test="not($front/date) and not (/rfc/@version >= 3)">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">&lt;date&gt; missing in reference '<xsl:value-of select="@anchor"/>' (note that it can be empty)</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:choose>
+    <xsl:when test="$front/date/@year != ''">
+      <xsl:if test="normalize-space($front/date)!=''">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">date element has both year attribute and text content: '<xsl:value-of select="$front/date"/>' in reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:if test="string(number($front/date/@year)) = 'NaN'">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">date/@year should be a number: '<xsl:value-of select="$front/date/@year"/>' in reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:text>, </xsl:text>
+      <xsl:if test="$front/date/@month!=''">
+        <xsl:if test="front/date/@day!='' and front/date/@x:include-day='true'">
+          <xsl:value-of select="front/date/@day"/>
+          <xsl:text> </xsl:text>
+        </xsl:if>
+        <xsl:choose>
+          <xsl:when test="not(local-name($front/..)='reference') and string(number($front/date/@month)) != 'NaN'">
+            <xsl:call-template name="get-month-as-name">
+              <xsl:with-param name="month" select="number($front/date/@month)"/>
+            </xsl:call-template>
+            <xsl:text> </xsl:text>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$front/date/@month"/><xsl:text> </xsl:text>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      <xsl:value-of select="$front/date/@year" />
+    </xsl:when>
+    <xsl:when test="document(x:source/@href)/rfc/front">
+      <!-- is the date element maybe included and should be defaulted? -->
+      <xsl:value-of select="concat(', ',$xml2rfc-ext-pub-month,' ',$xml2rfc-ext-pub-year)"/>
+    </xsl:when>
+    <xsl:when test="normalize-space($front/date)!=''">
+      <xsl:text>, </xsl:text>
+      <xsl:value-of select="normalize-space($front/date)"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="referencegroup">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <!-- check for reference to reference -->
+  <xsl:variable name="anchor" select="@anchor"/>
+  <xsl:choose>
+    <xsl:when test="not(@anchor)">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">missing anchor on reference: <xsl:value-of select="."/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(ancestor::ed:del) and (ancestor::rfc and not(key('xref-item',$anchor)))">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">unused reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(ancestor::ed:del) and (not(ancestor::rfc) and not($src//xref[@target=$anchor]))">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">unused (included) reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+
+  <xsl:call-template name="check-anchor"/>
+
+  <dt id="{@anchor}">
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:variable name="del-node" select="ancestor::ed:del"/>
+    <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+    <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+    <xsl:for-each select="../..">
+      <xsl:call-template name="insert-issue-pointer">
+        <xsl:with-param name="deleted-anchor" select="$deleted"/>
+      </xsl:call-template>
+    </xsl:for-each>
+    <xsl:call-template name="reference-name"/>
+  </dt>
+
+  <xsl:variable name="included" select="exslt:node-set($includeDirectives)/myns:include[@in=generate-id(current())]/reference"/>
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-sortrefs='yes' and $xml2rfc-symrefs!='no'">
+      <xsl:for-each select="reference|$included">
+        <xsl:sort select="concat(/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor,.//ed:ins//reference/@anchor)" />
+        <xsl:call-template name="insert-reference-body">
+          <xsl:with-param name="in-reference-group" select="true()"/>
+        </xsl:call-template>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:for-each select="reference|$included">
+        <xsl:call-template name="insert-reference-body">
+          <xsl:with-param name="in-reference-group" select="true()"/>
+        </xsl:call-template>
+      </xsl:for-each>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <xsl:if test="@target">
+    <dd>&lt;<a href="{@target}"><xsl:value-of select="@target"/></a>></dd>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="references">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:variable name="refseccount" select="count(/rfc/back/references)+count(/rfc/back/ed:replace/ed:ins/references)"/>
+
+  <xsl:choose>
+    <!-- handled in make-references -->
+    <xsl:when test="ancestor::references"/>
+    <!-- insert pseudo section when needed -->
+    <xsl:when test="not(preceding::references) and $refseccount!=1">
+      <xsl:call-template name="insert-conditional-hrule"/>
+      <section id="{$anchor-pref}references">
+        <xsl:call-template name="insert-conditional-pagebreak"/>
+        <xsl:variable name="sectionNumber">
+          <xsl:call-template name="get-references-section-number"/>
+        </xsl:variable>
+        <h2 id="{$anchor-pref}section.{$sectionNumber}">
+          <a href="#{$anchor-pref}section.{$sectionNumber}">
+            <xsl:call-template name="emit-section-number">
+              <xsl:with-param name="no" select="$sectionNumber"/>
+            </xsl:call-template>
+          </a>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-refparent"/>
+        </h2>
+        <xsl:if test="$sectionNumber!=''">
+          <xsl:call-template name="insert-errata">
+            <xsl:with-param name="section" select="$sectionNumber"/>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:for-each select=".|following-sibling::references">
+          <xsl:call-template name="make-references">
+            <xsl:with-param name="nested" select="true()"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </section>
+    </xsl:when>
+    <xsl:when test="preceding::references">
+      <!-- already processed -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="make-references">
+        <xsl:with-param name="nested" select="false()"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="make-references">
+  <xsl:param name="nested"/>
+  
+  <xsl:variable name="name">
+    <xsl:if test="ancestor::ed:del">
+      <xsl:text>del-</xsl:text>
+    </xsl:if>
+    <xsl:number level="any"/>
+  </xsl:variable>
+
+  <xsl:variable name="elemtype">
+    <xsl:choose>
+      <xsl:when test="$nested and count(ancestor::references)&gt;=2">h4</xsl:when>
+      <xsl:when test="$nested">h3</xsl:when>
+      <xsl:otherwise>h2</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="title">
+    <xsl:choose>
+      <xsl:when test="name">
+        <xsl:if test="@title">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">both @title attribute and name child node present</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:call-template name="render-name">
+          <xsl:with-param name="n" select="name/node()"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="not(@title) or @title=''">
+        <xsl:value-of select="$xml2rfc-refparent"/>
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">neither @title attribute nor name child node present, choosing default of '<xsl:value-of select="$xml2rfc-refparent"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="sectionNumber">
+    <xsl:call-template name="get-section-number"/>
+  </xsl:variable>
+
+  <xsl:variable name="anchorpostfix">
+    <xsl:if test="$nested">.<xsl:value-of select="$name"/></xsl:if>
+  </xsl:variable>
+
+  <section>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:if test="$name='1'">
+      <xsl:call-template name="insert-conditional-pagebreak"/>
+    </xsl:if>
+    <div id="{$anchor-pref}references{$anchorpostfix}">
+      <xsl:element name="{$elemtype}">
+        <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,'section.',$sectionNumber)"/></xsl:attribute>
+        <a href="#{$anchor-pref}section.{$sectionNumber}">
+          <xsl:call-template name="emit-section-number">
+            <xsl:with-param name="no" select="$sectionNumber"/>
+          </xsl:call-template>
+        </a>
+        <xsl:text> </xsl:text>
+        <xsl:copy-of select="$title"/>
+      </xsl:element>
+      <xsl:if test="$sectionNumber!=''">
+        <xsl:call-template name="insert-errata">
+          <xsl:with-param name="section" select="$sectionNumber"/>
+        </xsl:call-template>
+      </xsl:if>
+ 
+      <xsl:variable name="included" select="exslt:node-set($includeDirectives)/myns:include[@in=generate-id(current())]/*[self::reference or self::referencegroup]"/>
+      <xsl:variable name="refs" select="reference|referencegroup|ed:del|ed:ins|ed:replace|$included"/>
+      <xsl:choose>
+        <xsl:when test="references">
+          <xsl:for-each select="references">
+            <xsl:call-template name="make-references">
+              <xsl:with-param name="nested" select="true()"/>
+            </xsl:call-template>
+          </xsl:for-each>
+          <xsl:if test="$refs">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">Cannot mix &lt;references> elements with other child nodes such as <xsl:value-of select="local-name($refs[1])"/> (these will be ignored)</xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:when>
+        <xsl:when test="$refs">
+          <dl class="{$css-reference}">
+            <xsl:choose>
+              <xsl:when test="$xml2rfc-sortrefs='yes' and $xml2rfc-symrefs!='no'">
+                <xsl:apply-templates select="$refs">
+                  <xsl:sort select="concat(/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor,.//ed:ins//reference/@anchor)" />
+                </xsl:apply-templates>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:apply-templates select="$refs"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </dl>
+        </xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </div>
+  </section>
+</xsl:template>
+
+<xsl:template match="xi:include">
+  <xsl:choose>
+    <xsl:when test="not(parent::references) and not(parent::referencegroup)">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'Support for x:include is restricted to child elements of &lt;references> and &lt;referencegroup>'"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <!-- handled elsewhere -->
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- processed earlier -->
+<xsl:template match="references/name"/>
+
+<xsl:template match="rfc">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:variable name="ignored">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="//processing-instruction('rfc-ext')"/>
+      <xsl:with-param name="attr" select="'SANITYCHECK'"/>
+    </xsl:call-template>
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="//processing-instruction('rfc')"/>
+      <xsl:with-param name="attr" select="'SANITYCHECK'"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:variable name="lang">
+    <xsl:call-template name="get-lang" />
+  </xsl:variable>
+
+  <html lang="{$lang}">
+    <head>
+      <title>
+        <xsl:if test="$rfcno!=''">
+          <xsl:value-of select="concat('RFC ',$rfcno,' - ')"/>
+        </xsl:if>
+        <xsl:apply-templates select="front/title" mode="get-text-content" />
+      </title>
+      <xsl:call-template name="insertScripts" />
+      <xsl:choose>
+        <xsl:when test="$xml2rfc-ext-css-resource!='' and function-available('unparsed-text')">
+          <xsl:comment><xsl:value-of select="$xml2rfc-ext-css-resource"/></xsl:comment>
+          <style><xsl:value-of select="unparsed-text($xml2rfc-ext-css-resource,'UTF-8')"/></style>
+          <xsl:if test="$xml2rfc-ext-css-contents!=''">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">xml2rfc-ext-css-contents ignored, as xml2rfc-ext-css-resource was specified as well</xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:when>
+        <xsl:when test="$xml2rfc-ext-css-contents!=''">
+          <xsl:comment>Specified as xml2rfc-ext-css-contents</xsl:comment>
+          <style><xsl:value-of select="$xml2rfc-ext-css-contents"/></style>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:if test="$xml2rfc-ext-css-resource!=''">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">Support for css inclusion requires 'unparsed-text' function support (XSLT 2)</xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+          <xsl:call-template name="insertCss" />
+        </xsl:otherwise>
+      </xsl:choose>
+      <!-- <link rel="alternate stylesheet" media="screen" title="Plain (typewriter)" href="rfc2629tty.css" /> -->
+
+      <!-- link elements -->
+      <xsl:if test="$xml2rfc-toc='yes'">
+        <link rel="Contents" href="#{$anchor-pref}toc" />
+      </xsl:if>
+      <xsl:if test="$xml2rfc-authorship!='no'">
+        <link rel="Author" href="#{$anchor-pref}authors" />
+      </xsl:if>
+      <xsl:if test="$xml2rfc-private='' and not($src/rfc/@ipr='none')">
+        <xsl:choose>
+          <xsl:when test="$no-copylong">
+            <link rel="License" href="#{$anchor-pref}copyrightnotice" />
+          </xsl:when>
+          <xsl:otherwise>
+            <link rel="License" href="#{$anchor-pref}copyright" />
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      <xsl:if test="$has-index">
+        <link rel="Index" href="#{$anchor-pref}index" />
+      </xsl:if>
+      <xsl:apply-templates select="/" mode="links" />
+      <xsl:for-each select="x:link|link">
+        <link>
+          <xsl:choose>
+            <xsl:when test="self::x:link and @basename">
+              <xsl:attribute name="href">
+                <xsl:value-of select="concat(@basename,'.',$outputExtension)"/>
+              </xsl:attribute>
+              <xsl:copy-of select="@rel|@title" />
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:copy-of select="@*" />
+            </xsl:otherwise>
+          </xsl:choose>
+        </link>
+      </xsl:for-each>
+      <xsl:if test="$is-rfc">
+        <link rel="Alternate" title="Authoritative ASCII Version" href="http://www.ietf.org/rfc/rfc{$rfcno}.txt" />
+        <link rel="Help" title="RFC-Editor's Status Page" href="{$rfc-info-link}" />
+        <link rel="Help" title="Additional Information on tools.ietf.org" href="https://tools.ietf.org/html/rfc{$rfcno}"/>
+      </xsl:if>
+
+      <!-- viewport -->
+      <meta name="viewport" content="initial-scale=1"/>
+
+      <!-- generator -->
+      <xsl:if test="$xml2rfc-ext-include-generator!='no'">
+        <xsl:variable name="gen">
+          <xsl:call-template name="get-generator" />
+        </xsl:variable>
+        <meta name="generator" content="{$gen}" />
+      </xsl:if>
+
+      <!-- keywords -->
+      <xsl:if test="front/keyword">
+        <xsl:variable name="keyw">
+          <xsl:call-template name="get-keywords" />
+        </xsl:variable>
+        <meta name="keywords" content="{$keyw}" />
+      </xsl:if>
+
+      <xsl:if test="$xml2rfc-ext-support-rfc2731!='no'">
+        <!-- Dublin Core Metadata -->
+        <link rel="schema.dcterms" href="http://purl.org/dc/terms/" />
+
+        <!-- DC creator, see RFC2731 -->
+        <xsl:for-each select="front/author">
+          <xsl:variable name="initials">
+            <xsl:call-template name="get-author-initials"/>
+          </xsl:variable>
+          <xsl:variable name="surname">
+            <xsl:call-template name="get-author-surname"/>
+          </xsl:variable>
+          <xsl:variable name="disp">
+            <xsl:if test="$surname!=''">
+              <xsl:value-of select="$surname"/>
+              <xsl:if test="$initials!=''">
+                <xsl:text>, </xsl:text>
+                <xsl:value-of select="$initials"/>
+              </xsl:if>
+            </xsl:if>
+          </xsl:variable>
+          <xsl:if test="normalize-space($disp)!=''">
+            <meta name="dcterms.creator" content="{normalize-space($disp)}" />
+          </xsl:if>
+        </xsl:for-each>
+
+        <xsl:if test="$xml2rfc-private=''">
+          <xsl:choose>
+            <xsl:when test="$is-rfc">
+              <meta name="dcterms.identifier" content="urn:ietf:rfc:{$rfcno}" />
+            </xsl:when>
+            <xsl:when test="@docName">
+              <xsl:variable name="seq">
+                <xsl:call-template name="draft-sequence-number">
+                  <xsl:with-param name="name" select="@docName"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <xsl:if test="number($seq)>=0">
+                <meta name="dcterms.identifier" content="urn:ietf:id:{@docName}" />
+              </xsl:if>
+            </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+          <meta name="dcterms.issued">
+            <xsl:attribute name="content">
+              <xsl:value-of select="concat($xml2rfc-ext-pub-year,'-',$pub-month-numeric)"/>
+              <xsl:if test="$xml2rfc-ext-pub-day != '' and not($is-rfc)">
+                <xsl:value-of select="concat('-',format-number($xml2rfc-ext-pub-day,'00'))"/>
+              </xsl:if>
+            </xsl:attribute>
+          </meta>
+
+          <xsl:if test="@obsoletes!=''">
+            <xsl:call-template name="rfclist-for-dcmeta">
+              <xsl:with-param name="list" select="@obsoletes"/>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:if>
+
+        <xsl:if test="front/abstract">
+          <meta name="dcterms.abstract" content="{normalize-space(front/abstract)}" />
+        </xsl:if>
+
+        <xsl:if test="$is-rfc">
+          <meta name="dcterms.isPartOf" content="urn:issn:2070-1721" />
+        </xsl:if>
+
+      </xsl:if>
+
+      <!-- this replicates dcterms.abstract, but is used by Google & friends -->
+      <xsl:if test="front/abstract">
+        <meta name="description" content="{normalize-space(front/abstract)}" />
+      </xsl:if>
+    </head>
+
+    <xsl:call-template name="body" />
+  </html>
+</xsl:template>
+
+<xsl:template name="body">
+  <body>
+    <!-- insert onload scripts, when required -->
+    <xsl:variable name="onload">
+      <xsl:if test="$xml2rfc-ext-insert-metadata='yes' and $is-rfc">getMeta("<xsl:value-of select="$rfcno"/>","rfc.meta");</xsl:if>
+      <xsl:if test="$xml2rfc-ext-insert-metadata='yes' and /rfc/@docName">
+        <xsl:if test="$is-submitted-draft">getMeta("<xsl:value-of select="$draft-basename"/>","<xsl:value-of select="$draft-seq"/>","rfc.meta");</xsl:if>
+      </xsl:if>
+      <xsl:if test="/rfc/x:feedback">initFeedback();</xsl:if>
+      <xsl:if test="$xml2rfc-ext-refresh-from!=''">RfcRefresh.initRefresh()</xsl:if>
+    </xsl:variable>
+    <xsl:if test="$onload!=''">
+      <xsl:attribute name="onload">
+        <xsl:value-of select="$onload"/>
+      </xsl:attribute>
+    </xsl:if>
+
+    <xsl:call-template name="add-start-material" />
+
+    <!-- insert diagnostics -->
+    <xsl:call-template name="insert-diagnostics"/>
+
+    <xsl:apply-templates select="front" />
+    <xsl:apply-templates select="middle" />
+    <xsl:call-template name="back" />
+
+    <xsl:call-template name="add-end-material" />
+  </body>
+</xsl:template>
+
+<xsl:template match="t">
+  <xsl:param name="inherited-self-link"/>
+
+  <xsl:variable name="textcontent" select="normalize-space(.)"/>
+  <xsl:variable name="endswith" select="substring($textcontent,string-length($textcontent))"/>
+  <xsl:variable name="keepwithnext" select="$endswith=':' or @keepWithNext='true'"/>
+  <xsl:variable name="keepwithprevious" select="@keepWithPrevious='true'"/>
+
+  <xsl:variable name="stype">
+    <xsl:choose>
+      <xsl:when test="ancestor::abstract">abstract</xsl:when>
+      <xsl:when test="ancestor::note">note</xsl:when>
+      <xsl:when test="ancestor::boilerplate">boilerplate</xsl:when>
+      <xsl:otherwise>section</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:if test="preceding-sibling::section or preceding-sibling::appendix">
+    <xsl:call-template name="inline-warning">
+      <xsl:with-param name="msg">The paragraph below is misplaced; maybe a section is closed in the wrong place: </xsl:with-param>
+      <xsl:with-param name="msg2"><xsl:value-of select="."/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  
+  <xsl:variable name="class">
+    <xsl:if test="$keepwithnext">avoidbreakafter</xsl:if>
+    <xsl:text> </xsl:text>
+    <xsl:if test="$keepwithprevious">avoidbreakbefore</xsl:if>
+  </xsl:variable>
+
+  <div>
+    <xsl:if test="not(ancestor::list)">
+      <xsl:call-template name="attach-paragraph-number-as-id"/>
+    </xsl:if>
+    <xsl:if test="normalize-space($class)!=''">
+      <xsl:attribute name="class"><xsl:value-of select="normalize-space($class)"/></xsl:attribute>
+    </xsl:if>
+    <xsl:if test="@indent and number(@indent)&gt;0">
+      <xsl:attribute name="style">padding-left: <xsl:value-of select="@indent div 2"/>em</xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates mode="t-content" select="node()[1]">
+      <xsl:with-param name="inherited-self-link" select="$inherited-self-link"/>
+      <xsl:with-param name="anchor" select="@anchor"/>
+    </xsl:apply-templates>
+  </div>
+</xsl:template>
+
+<!-- for t-content, dispatch to default templates if it's block-level content -->
+<xsl:template mode="t-content" match="list|figure|texttable">
+  <!-- <xsl:comment>t-content block-level</xsl:comment>  -->
+  <xsl:apply-templates select="." />
+  <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content" />
+</xsl:template>
+
+<!-- ... otherwise group into p elements -->
+<xsl:template mode="t-content" match="node()">
+  <xsl:param name="inherited-self-link"/>
+  <xsl:param name="anchor"/>
+
+  <xsl:variable name="p">
+    <xsl:for-each select="..">
+      <xsl:call-template name="get-paragraph-number" />
+    </xsl:for-each>
+  </xsl:variable>
+
+  <xsl:if test="not(self::text() and normalize-space(.)='' and not(following-sibling::node()))">
+    <xsl:variable name="textcontent">
+      <xsl:apply-templates mode="t-content2" select="." />
+    </xsl:variable>
+
+    <xsl:choose>
+      <!-- do not open a new p element if this is a whitespace-only text node and no siblings follow -->
+      <xsl:when test="normalize-space($textcontent)!=''">
+        <p>
+          <xsl:if test="$anchor!=''">
+            <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+          </xsl:if>
+          <xsl:variable name="stype">
+            <xsl:choose>
+              <xsl:when test="ancestor::abstract">abstract</xsl:when>
+              <xsl:when test="ancestor::note">note</xsl:when>
+              <xsl:when test="ancestor::boilerplate">boilerplate</xsl:when>
+              <xsl:otherwise>section</xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <xsl:variable name="anch">
+            <xsl:if test="$p!='' and not(ancestor::li) and not(ancestor::x:lt) and not(preceding-sibling::node())">
+              <xsl:value-of select="concat($anchor-pref,$stype,'.',$p)"/>
+            </xsl:if>
+          </xsl:variable>
+          <xsl:call-template name="insertInsDelClass"/>
+          <xsl:call-template name="editingMark" />
+          <xsl:apply-templates mode="t-content2" select="." />
+          <xsl:if test="$xml2rfc-ext-paragraph-links='yes'">
+            <xsl:if test="$anch!=''">
+              <a class='self' href='#{$anch}'>&#xb6;</a>
+            </xsl:if>
+            <xsl:if test="$inherited-self-link!=''">
+              <a class='self' href='#{$inherited-self-link}'>&#xb6;</a>
+            </xsl:if>
+          </xsl:if>
+        </p>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- but we still need to emit non textual content, such as irefs -->
+        <xsl:apply-templates mode="t-content2" select="." />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+  <xsl:apply-templates mode="t-content" select="following-sibling::*[self::list or self::figure or self::texttable][1]" />
+</xsl:template>
+
+<xsl:template mode="t-content2" match="*">
+  <xsl:apply-templates select="." />
+  <xsl:if test="not(following-sibling::node()[1] [self::list or self::figure or self::texttable])">
+    <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content2" />
+  </xsl:if>
+</xsl:template>
+
+<xsl:template mode="t-content2" match="text()">
+  <xsl:apply-templates select="." />
+  <xsl:if test="not(following-sibling::node()[1] [self::list or self::figure or self::texttable])">
+    <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content2" />
+  </xsl:if>
+</xsl:template>
+
+<xsl:template mode="t-content2" match="comment()|processing-instruction()">
+  <xsl:apply-templates select="." />
+  <xsl:if test="not(following-sibling::node()[1] [self::list or self::figure or self::texttable])">
+    <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content2" />
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="title">
+  <xsl:variable name="t" select="normalize-space(.)"/>
+  <xsl:variable name="tlen" select="string-length($t)"/>
+  <xsl:variable name="alen" select="string-length(@abbrev)"/>
+
+  <xsl:if test="@abbrev and $alen > 40">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">title/@abbrev too long (max 40 characters)</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$tlen > 40 and (not(@abbrev) or @abbrev='')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">title too long, should supply title/@abbrev attribute with less than 40 characters</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$tlen &lt;= 40 and @abbrev!=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">title/@abbrev was specified despite the title being short enough (<xsl:value-of select="$tlen"/>)</xsl:with-param>
+      <xsl:with-param name="msg2">Title: '<xsl:value-of select="normalize-space($t)"/>', abbreviated title='<xsl:value-of select="@abbrev"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:apply-templates />
+</xsl:template>
+
+<xsl:template name="insertTitle">
+  <xsl:choose>
+    <xsl:when test="@ed:old-title">
+      <del>
+        <xsl:if test="ancestor-or-self::*[@ed:entered-by] and @ed:datetime">
+          <xsl:attribute name="title"><xsl:value-of select="concat(@ed:datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="@ed:old-title"/>
+      </del>
+      <ins>
+        <xsl:if test="ancestor-or-self::*[@ed:entered-by] and @ed:datetime">
+          <xsl:attribute name="title"><xsl:value-of select="concat(@ed:datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="@title"/>
+      </ins>
+    </xsl:when>
+    <xsl:when test="name">
+      <xsl:if test="@title">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">both @title attribute and name child node present</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="render-name">
+        <xsl:with-param name="n" select="name/node()"/>
+        <xsl:with-param name="strip-links" select="not(ancestor-or-self::figure)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="@title"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- irefs that are section-level thus can use the section anchor -->
+<xsl:variable name="section-level-irefs" select="//section/iref[count(preceding-sibling::*[not(self::iref) and not(self::x:anchor-alias) and not(self::name)])=0]"/>
+
+<!-- suppress xml2rfc preptool artefacts -->
+<xsl:template match="section[author]"/>
+
+<xsl:template match="section|appendix">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:if test="self::appendix">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The "appendix" element is deprecated, use "section" inside "back" instead.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="sectionNumber">
+    <xsl:choose>
+      <xsl:when test="ancestor::boilerplate"></xsl:when>
+      <xsl:otherwise><xsl:call-template name="get-section-number" /></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:if test="not(ancestor::section) and not(ancestor::boilerplate)">
+    <xsl:call-template name="insert-conditional-hrule"/>
+  </xsl:if>
+
+  <xsl:variable name="elemtype">
+    <xsl:choose>
+      <xsl:when test="count(ancestor::section) &lt;= 3">h<xsl:value-of select="2 + count(ancestor::section)"/></xsl:when>
+      <xsl:otherwise>h6</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:if test="$xml2rfc-ext-insert-metadata='yes' and ($is-rfc or $is-submitted-draft) and @anchor='rfc.status'">
+    <aside id="{$anchor-pref}meta" class="{$css-docstatus}"></aside>
+  </xsl:if>
+
+  <xsl:variable name="classes"><xsl:if test="@removeInRFC='true'">rfcEditorRemove</xsl:if></xsl:variable>
+
+  <section>
+    <xsl:call-template name="copy-anchor"/>
+
+    <xsl:if test="normalize-space($classes)!=''">
+      <xsl:attribute name="class"><xsl:value-of select="normalize-space($classes)"/></xsl:attribute>
+    </xsl:if>
+    
+    <xsl:element name="{$elemtype}">
+      <xsl:if test="$sectionNumber!=''">
+        <xsl:attribute name="id"><xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$sectionNumber"/></xsl:attribute>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="$sectionNumber='1' or $sectionNumber='A'">
+          <!-- pagebreak, this the first section -->
+          <xsl:attribute name="class">np</xsl:attribute>
+        </xsl:when>
+        <xsl:when test="not(ancestor::section) and not(ancestor::boilerplate)">
+          <xsl:call-template name="insert-conditional-pagebreak"/>
+        </xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+
+      <xsl:call-template name="insertInsDelClass" />
+
+      <xsl:if test="$sectionNumber!='' and not(contains($sectionNumber,$unnumbered))">
+        <a href="#{$anchor-pref}section.{$sectionNumber}">
+          <xsl:call-template name="emit-section-number">
+            <xsl:with-param name="no" select="$sectionNumber"/>
+            <xsl:with-param name="appendixPrefix" select="true()"/>
+          </xsl:call-template>
+        </a>
+        <xsl:text>&#0160;</xsl:text>
+      </xsl:if>
+
+      <!-- issue tracking? -->
+      <xsl:if test="@ed:resolves">
+        <xsl:call-template name="insert-issue-pointer"/>
+      </xsl:if>
+
+      <xsl:call-template name="check-anchor"/>
+      <xsl:variable name="anchor">
+        <xsl:choose>
+          <xsl:when test="@anchor"><xsl:value-of select="@anchor"/></xsl:when>
+          <xsl:otherwise><xsl:call-template name="sluggy-anchor"/></xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+
+      <xsl:variable name="name">
+        <xsl:choose>
+          <xsl:when test="starts-with(@title,'Since ')">
+            <xsl:value-of select="substring-after(@title,'Since ')"/>
+          </xsl:when>
+          <xsl:when test="starts-with(@title,'draft-')">
+            <xsl:value-of select="@title"/>
+          </xsl:when>
+          <xsl:otherwise/>
+        </xsl:choose>
+      </xsl:variable>
+      
+      <xsl:variable name="basename">
+        <xsl:call-template name="draft-base-name">
+          <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:variable name="seq">
+        <xsl:call-template name="draft-sequence-number">
+          <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+      </xsl:variable>
+
+      <xsl:variable name="offset">
+        <xsl:choose>
+          <xsl:when test="starts-with(@title,'Since ')">1</xsl:when>        
+          <xsl:otherwise>0</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      
+      <xsl:variable name="smells-like-change-log" select="ancestor-or-self::section[@removeInRFC='true'] or ancestor::section[@title='Changes'] or ancestor::section[@title='Change Log']"/>
+      
+      <xsl:variable name="diff-uri">
+        <xsl:if test="$smells-like-change-log and $basename!=''">
+          <xsl:variable name="next" select="concat($basename,'-',format-number($offset + $seq,'00'))"/>
+          <xsl:choose>
+            <!-- check whether the "next" draft exists (is mentioned in a sibling section -->
+            <xsl:when test="../section[contains(@title,$next)]">
+              <xsl:call-template name="compute-diff-uri">
+                <xsl:with-param name="name" select="$next"/>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="starts-with(ancestor::rfc/@docName,$basename)">
+              <xsl:call-template name="compute-latest-diff-uri">
+                <xsl:with-param name="name" select="$basename"/>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </xsl:if>
+      </xsl:variable>
+
+      <xsl:variable name="text-uri">
+        <xsl:if test="$smells-like-change-log and $basename!=''">
+          <xsl:call-template name="compute-internet-draft-uri">
+            <xsl:with-param name="internet-draft" select="$name"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:variable>
+
+      <xsl:choose>
+        <xsl:when test="$anchor!=''">
+          <a href="#{$anchor}"><xsl:call-template name="insertTitle"/></a>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="insertTitle"/>
+        </xsl:otherwise>
+      </xsl:choose>
+
+      <xsl:if test="$xml2rfc-ext-paragraph-links='yes' and $text-uri!=''">
+        <xsl:text> </xsl:text>
+        <a class="self" href="{$text-uri}" title="plain text">&#x1f4c4;</a>
+      </xsl:if>
+      <xsl:if test="$xml2rfc-ext-paragraph-links='yes' and $diff-uri!=''">
+        <xsl:text> </xsl:text>
+        <a class="self" href="{$diff-uri}" title="diffs">&#x1f50d;</a>
+      </xsl:if>
+    </xsl:element>
+
+    <xsl:if test="$sectionNumber!=''">
+      <xsl:call-template name="insert-errata">
+        <xsl:with-param name="section" select="$sectionNumber"/>
+      </xsl:call-template>
+    </xsl:if>
+    
+    <xsl:if test="@removeInRFC='true' and (not(t) or t[1]!=$section-removeInRFC)">
+      <xsl:variable name="t">
+        <t><xsl:value-of select="$section-removeInRFC"/></t>
+      </xsl:variable>
+      <xsl:variable name="link" select="concat($anchor-pref,'section.',$sectionNumber,'.p.1')"/>
+      <div id="{$link}">
+        <xsl:apply-templates mode="t-content" select="exslt:node-set($t)//text()">
+          <xsl:with-param name="inherited-self-link" select="$link"/>
+        </xsl:apply-templates>
+      </div>
+    </xsl:if>
+
+    <!-- continue with all child elements but the irefs processed above -->
+    <xsl:for-each select="*">
+      <xsl:if test="count(.|$section-level-irefs)!=count($section-level-irefs)">
+        <xsl:apply-templates select="."/>
+      </xsl:if>
+    </xsl:for-each>
+  </section>
+</xsl:template>
+
+<!-- errata handling -->
+<xsl:template name="insert-errata">
+  <xsl:param name="section"/>
+  <xsl:variable name="es" select="$errata-parsed[section=$section or (not(section) and $section='1')]"/>
+  <xsl:if test="$es">
+    <aside class="{$css-erratum}">
+      <xsl:for-each select="$es">
+        <xsl:sort select="@eid" data-type="number"/>
+        <div>
+          <xsl:variable name="tooltip">
+            <xsl:value-of select="@reported-by"/>
+            <xsl:text>, </xsl:text>
+            <xsl:value-of select="@reported"/>
+            <xsl:if test="@type"> (<xsl:value-of select="@type"/>)</xsl:if>
+          </xsl:variable>
+          <xsl:variable name="uri">
+            <xsl:call-template name="compute-rfc-erratum-uri">
+              <xsl:with-param name="eid" select="@eid"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <a href="{$uri}" title="{$tooltip}">Erratum <xsl:value-of select="@eid"/></a>
+          <xsl:choose>
+            <xsl:when test="@status='Verified'"><xsl:text> </xsl:text><span title="verified">&#x2714;</span></xsl:when>
+            <xsl:when test="@status='Reported'"><xsl:text> </xsl:text><span title="reported">&#x2709;</span></xsl:when>
+            <xsl:when test="@status='Held for Document Update'"><xsl:text> </xsl:text><span title="held for update">&#x2700;</span></xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </div>
+      </xsl:for-each>
+    </aside>
+  </xsl:if>
+</xsl:template>
+
+<!-- already processed by insertTitle -->
+<xsl:template match="note/name"/>
+<xsl:template match="section/name"/>
+
+<xsl:template match="spanx[@style='emph' or not(@style)]|em">
+  <em>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </em>
+</xsl:template>
+
+<xsl:template match="spanx[@style='verb' or @style='vbare']|tt">
+  <span class="tt">
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </span>
+</xsl:template>
+
+<xsl:template match="spanx[@style='strong']|strong">
+  <strong>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </strong>
+</xsl:template>
+
+<xsl:template match="spanx[@style!='']" priority="0.1">
+  <xsl:call-template name="warning">
+    <xsl:with-param name="msg">unknown spanx style attribute '<xsl:value-of select="@style"/>' ignored</xsl:with-param>
+  </xsl:call-template>
+  <span>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </span>
+</xsl:template>
+
+<xsl:template name="insert-blank-lines">
+  <xsl:param name="no"/>
+  <xsl:choose>
+    <xsl:when test="$no >= $xml2rfc-ext-vspace-pagebreak">
+      <br/>
+      <!-- done; this probably was an attempt to generate a pagebreak -->
+    </xsl:when>
+    <xsl:when test="$no &lt;= 0">
+      <br/>
+      <!-- done -->
+    </xsl:when>
+    <xsl:otherwise>
+      <br/>
+      <xsl:call-template name="insert-blank-lines">
+        <xsl:with-param name="no" select="$no - 1"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="vspace[not(@blankLines)]">
+  <br />
+</xsl:template>
+
+<xsl:template match="vspace">
+  <xsl:call-template name="insert-blank-lines">
+    <xsl:with-param name="no" select="@blankLines"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="br">
+  <br/>
+</xsl:template>
+
+<!-- keep the root for the case when we process XSLT-inline markup -->
+<xsl:variable name="src" select="/" />
+
+<xsl:template name="render-section-ref">
+  <xsl:param name="from" />
+  <xsl:param name="to" />
+
+  <xsl:variable name="refname">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-section-type"/>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name="refnum">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-section-number" />
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$from/@format='counter'">
+      <xsl:choose>
+        <xsl:when test="$to/self::abstract">
+          <xsl:call-template name="error">
+            <xsl:with-param name="inline">no</xsl:with-param>
+            <xsl:with-param name="msg">xref to abstract with format='counter' not allowed</xsl:with-param>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$refnum"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="$from/@format='title'">
+      <xsl:choose>
+        <xsl:when test="$to/name">
+          <xsl:call-template name="render-name-ref">
+            <xsl:with-param name="n" select="$to/name/node()"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:when test="$to/@title">
+          <xsl:value-of select="normalize-space($to/@title)"/>
+        </xsl:when>
+        <xsl:when test="$to/self::abstract">Abstract</xsl:when>
+        <xsl:when test="$to/self::references">References</xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="$from/@format='none'">
+      <!-- Nothing to do -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:choose>
+        <xsl:when test="starts-with($refnum,$unnumbered)">
+          <xsl:value-of select="$refname"/>
+          <xsl:text> "</xsl:text>
+          <xsl:choose>
+            <xsl:when test="$to/name">
+              <xsl:apply-templates select="$to/name/node()"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$to/@title"/>
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="normalize-space(concat($refname,' ',$refnum))"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-section-xref-format">
+  <xsl:param name="default"/>
+  <xsl:choose>
+    <xsl:when test="self::relref and (*|text())">
+      <xsl:if test="@displayFormat">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">@displayFormat is ignored on &lt;relref> with content</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="self::relref">
+      <xsl:choose>
+        <xsl:when test="not(@displayFormat)">of</xsl:when>
+        <xsl:when test="@displayFormat='parens' or @displayFormat='of' or @displayFormat='comma' or @displayFormat='bare'">
+          <xsl:value-of select="@displayFormat"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">unknown format for @displayFormat: <xsl:value-of select="@displayFormat"/></xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="self::xref and @sectionFormat">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">@sectionFormat is deprecated, use @x:fmt instead</xsl:with-param>
+      </xsl:call-template>
+      <xsl:if test="@x:fmt">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">both @x:fmt and @sectionFormat specified</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="@sectionFormat='of' or @sectionFormat='comma' or @sectionFormat='parens' or @sectionFormat='bare'">
+          <xsl:value-of select="@sectionFormat"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">unknown format for @sectionFormat</xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="self::xref and @x:fmt">
+      <xsl:choose>
+        <xsl:when test="@x:fmt='()'">parens</xsl:when>
+        <xsl:when test="@x:fmt='of'">of</xsl:when>
+        <xsl:when test="@x:fmt=','">comma</xsl:when>
+        <xsl:when test="@x:fmt='none'">none</xsl:when>
+        <xsl:when test="@x:fmt='sec'">section</xsl:when>
+        <xsl:when test="@x:fmt='number'">bare</xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">unknown format for @x:fmt</xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$default"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-section-xref-section">
+  <xsl:choose>
+    <xsl:when test="@section">
+      <xsl:value-of select="@section"/>
+    </xsl:when>
+    <xsl:when test="@x:sec">
+      <xsl:value-of select="@x:sec"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="xref[*|text()]|relref[*|text()]">
+
+  <xsl:variable name="xref" select="."/>
+ 
+  <xsl:variable name="target">
+    <xsl:call-template name="get-target-anchor"/>
+  </xsl:variable>
+  <xsl:variable name="node" select="key('anchor-item',$target)" />
+  <xsl:variable name="anchor"><xsl:value-of select="$anchor-pref"/>xref.<xsl:value-of select="$target"/>.<xsl:number level="any" count="xref[@target=$target]|relref[@target=$target]"/></xsl:variable>
+
+  <xsl:if test="@target!=$target">
+    <xsl:call-template name="info">
+      <xsl:with-param name="msg">Target '<xsl:value-of select="@target"/>' rewritten to '<xsl:value-of select="$target"/>'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="sfmt">
+    <xsl:call-template name="get-section-xref-format"/>
+  </xsl:variable>
+
+  <xsl:variable name="ssec">
+    <xsl:call-template name="get-section-xref-section"/>
+  </xsl:variable>
+
+  <xsl:variable name="href">
+    <xsl:call-template name="computed-target">
+      <xsl:with-param name="bib" select="$node"/>
+      <xsl:with-param name="ref" select="."/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="self::relref and not($node/self::reference)">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">relref/@target must be a reference</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="self::relref and $href=''">
+      <xsl:apply-templates/>
+    </xsl:when>
+    <xsl:when test="self::relref">
+      <xsl:call-template name="emit-link">
+        <xsl:with-param name="target" select="$href"/>
+        <xsl:with-param name="id" select="$anchor"/>
+        <xsl:with-param name="child-nodes" select="*|text()"/>
+        <xsl:with-param name="index-item" select="$target"/>
+        <xsl:with-param name="index-subitem" select="$ssec"/>
+      </xsl:call-template>
+    </xsl:when>
+  
+    <!-- $sfmt='none': do not generate any links -->
+    <xsl:when test="$sfmt='none'">
+      <xsl:choose>
+        <xsl:when test="$node/self::reference">
+          <xsl:call-template name="emit-link">
+            <xsl:with-param name="id" select="$anchor"/>
+            <xsl:with-param name="citation-title" select="normalize-space($node/front/title)"/>
+            <xsl:with-param name="child-nodes" select="*|text()"/>
+            <xsl:with-param name="index-item" select="$target"/>
+            <xsl:with-param name="index-subitem" select="$ssec"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+
+    <!-- Other $sfmt values than "none": unsupported -->
+    <xsl:when test="$sfmt!='' and $sfmt!='none'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('ignoring unknown xref section format extension: ',$sfmt)"/>
+      </xsl:call-template>
+    </xsl:when>
+
+    <!-- Section links -->
+    <xsl:when test="$node/self::section or $node/self::appendix">
+      <xsl:choose>
+        <xsl:when test="@format='none' or $xml2rfc-ext-xref-with-text-generate='nothing'">
+          <xsl:call-template name="emit-link">
+            <xsl:with-param name="target" select="concat('#',$target)"/>
+            <xsl:with-param name="id">
+              <xsl:if test="//iref[@x:for-anchor=$target] | //iref[@x:for-anchor='' and ../@anchor=$target]"><xsl:value-of select="$anchor"/></xsl:if>
+            </xsl:with-param>
+            <xsl:with-param name="child-nodes" select="*|text()"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- index links to this xref -->
+          <xsl:variable name="ireftargets" select="key('iref-xanch',$target) | key('iref-xanch','')[../@anchor=$target]"/>
+
+          <xsl:apply-templates/>
+          <xsl:text> (</xsl:text>
+          <xsl:call-template name="xref-to-section">
+            <xsl:with-param name="from" select="$xref"/>
+            <xsl:with-param name="to" select="$node"/>
+            <xsl:with-param name="id" select="$anchor"/>
+            <xsl:with-param name="irefs" select="$ireftargets"/>
+          </xsl:call-template>
+          <xsl:text>)</xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+
+    <xsl:when test="$node/self::cref and $node/@display='false'">
+      <xsl:for-each select="$xref">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="concat('Comment ',$node/@anchor,' is hidden and thus can not be referenced')"/>
+        </xsl:call-template>
+      </xsl:for-each>
+    </xsl:when>
+
+    <xsl:when test="$node/self::cref and $xml2rfc-comments='no'">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">xref to cref, but comments aren't included in the output</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+
+    <xsl:otherwise>
+      <!-- check normative/informative -->
+      <xsl:variable name="t-is-normative" select="ancestor-or-self::*[@x:nrm][1]"/>
+      <xsl:variable name="is-normative" select="$t-is-normative/@x:nrm='true'"/>
+      <xsl:if test="count($node)=1 and $is-normative">
+        <xsl:variable name="t-r-is-normative" select="$node/ancestor-or-self::*[@x:nrm][1]"/>
+        <xsl:variable name="r-is-normative" select="$t-r-is-normative/@x:nrm='true'"/>
+        <xsl:if test="not($r-is-normative)">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg" select="concat('Potentially normative reference to ',$target,' not referenced normatively')"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:if>
+
+      <xsl:call-template name="emit-link">
+        <xsl:with-param name="target" select="concat('#',$target)"/>
+        <xsl:with-param name="id">
+          <xsl:if test="@format='none'"><xsl:value-of select="$anchor"/></xsl:if>
+        </xsl:with-param>
+        <xsl:with-param name="child-nodes" select="*|text()"/>
+      </xsl:call-template>
+
+      <xsl:if test="not(@format='none' or $xml2rfc-ext-xref-with-text-generate='nothing')">
+        <xsl:for-each select="$src/rfc/back/references//reference[@anchor=$target]">
+          <xsl:text> </xsl:text>
+          <xsl:call-template name="emit-link">
+            <xsl:with-param name="citation-title" select="normalize-space(front/title)"/>
+            <xsl:with-param name="id" select="$anchor"/>
+            <xsl:with-param name="text">
+              <xsl:call-template name="reference-name"/>
+            </xsl:with-param>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:key name="iref-xanch" match="iref[@x:for-anchor]" use="@x:for-anchor"/>
+
+<!-- xref to section or appendix -->
+<xsl:template name="xref-to-section">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+  <xsl:param name="id"/>
+  <xsl:param name="irefs"/>
+  
+  <a href="#{$from/@target}">
+    <xsl:if test="$irefs">
+      <!-- insert id when a backlink to this xref is needed in the index -->
+      <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+    </xsl:if>
+    <xsl:attribute name="title">
+      <xsl:call-template name="get-title-as-string">
+        <xsl:with-param name="node" select="$to"/>
+      </xsl:call-template>
+    </xsl:attribute>
+    <xsl:call-template name="render-section-ref">
+      <xsl:with-param name="from" select="$from"/>
+      <xsl:with-param name="to" select="$to"/>
+    </xsl:call-template>
+  </a>
+</xsl:template>
+
+<!-- xref to figure -->
+<xsl:template name="xref-to-figure-text">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+
+  <xsl:variable name="figcnt">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-figure-number"/>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$from/@format='counter'">
+      <xsl:value-of select="$figcnt" />
+    </xsl:when>
+    <xsl:when test="$from/@format='none'">
+      <!-- Nothing to do -->
+    </xsl:when>
+    <xsl:when test="$from/@format='title'">
+      <xsl:choose>
+        <xsl:when test="$to/name">
+          <xsl:call-template name="render-name-ref">
+            <xsl:with-param name="n" select="$to/name/node()"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="normalize-space($to/@title)" />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="normalize-space(concat('Figure ',$figcnt))"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="xref-to-figure">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+
+  <xsl:variable name="title">
+    <xsl:call-template name="get-title-as-string">
+      <xsl:with-param name="node" select="$to"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <a href="#{$from/@target}">
+    <xsl:if test="$title!=''">
+      <xsl:attribute name="title">
+        <xsl:value-of select="$title"/>
+      </xsl:attribute>
+    </xsl:if>
+    <xsl:call-template name="xref-to-figure-text">
+      <xsl:with-param name="from" select="$from"/>
+      <xsl:with-param name="to" select="$to"/>
+    </xsl:call-template>
+  </a>
+</xsl:template>
+
+<!-- xref to table -->
+<xsl:template name="xref-to-table-text">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+
+  <xsl:variable name="tabcnt">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-table-number"/>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$from/@format='counter'">
+      <xsl:value-of select="$tabcnt" />
+    </xsl:when>
+    <xsl:when test="$from/@format='none'">
+      <!-- Nothing to do -->
+    </xsl:when>
+    <xsl:when test="$from/@format='title'">
+      <xsl:choose>
+        <xsl:when test="$to/self::table">
+          <xsl:call-template name="render-name-ref">
+            <xsl:with-param name="n" select="$to/name/node()"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$to/@title" />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="normalize-space(concat('Table ',$tabcnt))"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="xref-to-table">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+
+  <xsl:variable name="title">
+    <xsl:call-template name="get-title-as-string">
+      <xsl:with-param name="node" select="$to"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <a href="#{$from/@target}">
+    <xsl:if test="$title!=''">
+      <xsl:attribute name="title">
+        <xsl:value-of select="$title"/>
+      </xsl:attribute>
+    </xsl:if>
+    <xsl:call-template name="xref-to-table-text">
+      <xsl:with-param name="from" select="$from"/>
+      <xsl:with-param name="to" select="$to"/>
+    </xsl:call-template>
+  </a>
+</xsl:template>
+
+<!-- xref to paragraph -->
+<xsl:template name="xref-to-paragraph-text">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+
+  <xsl:variable name="tcnt">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-paragraph-number" />
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name="pparent" select="$to/.."/>
+  <xsl:variable name="listtype">
+    <xsl:choose>
+      <xsl:when test="$pparent/self::list">
+        <xsl:value-of select="$pparent/@style"/>
+      </xsl:when>
+      <xsl:when test="$pparent/self::dl">definition</xsl:when> 
+      <xsl:when test="$pparent/self::ol[@type='a']">letters</xsl:when> 
+      <xsl:when test="$pparent/self::ol[@type='A']">Letters</xsl:when> 
+      <xsl:when test="$pparent/self::ol[@type='i']">rnumbers</xsl:when> 
+      <xsl:when test="$pparent/self::ol[@type='I']">Rnumbers</xsl:when> 
+      <xsl:when test="$pparent/self::ol[string-length(@type)>1]">format <xsl:value-of select="$pparent/self::ol/@type"/></xsl:when> 
+      <xsl:when test="$pparent/self::ol">numbers</xsl:when> 
+      <xsl:otherwise></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="s">
+    <xsl:choose>
+      <xsl:when test="$pparent/self::ol and $pparent/@group">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="$pparent"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="$pparent/self::list and $pparent/@counter">
+        <xsl:for-each select="$pparent">
+          <xsl:value-of select="1 + count(preceding::list[@counter=$pparent/@counter]/*)"/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:when test="$pparent/self::ol and $pparent/@start">
+        <xsl:value-of select="$pparent/@start"/>
+      </xsl:when>
+      <xsl:otherwise>1</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="n">
+    <xsl:for-each select="$to">
+      <xsl:number/>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name="format">
+    <xsl:choose>
+      <xsl:when test="$listtype='letters'">a</xsl:when>
+      <xsl:when test="$listtype='Letters'">A</xsl:when>
+      <xsl:when test="$listtype='rnumbers'">i</xsl:when>
+      <xsl:when test="$listtype='Rnumbers'">I</xsl:when>
+      <xsl:otherwise>1</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="listindex">
+    <xsl:choose>
+      <xsl:when test="starts-with($listtype,'format ')">
+        <xsl:call-template name="expand-format-percent">
+          <xsl:with-param name="format" select="substring-after($listtype,'format ')"/>
+          <xsl:with-param name="pos" select="$n + $s - 1"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:number value="$n + $s - 1" format="{$format}"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$from/@format='counter'">
+      <xsl:choose>
+        <xsl:when test="$listtype!='' and $listindex!=''">
+          <xsl:value-of select="$listindex"/>
+        </xsl:when>
+        <xsl:when test="$listtype!='' and $listindex=''">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg" select="concat('Use of format=counter for unsupported list type ',$listtype)"/>
+          </xsl:call-template>
+          <xsl:value-of select="$tcnt"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$tcnt"/>              
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="$from/@format='none'">
+      <!-- Nothing to do -->
+    </xsl:when>
+    <xsl:when test="$from/@format='title'">
+      <xsl:choose>
+        <xsl:when test="$to/self::dt">
+          <xsl:apply-templates select="$to/node()"/>
+        </xsl:when>
+        <xsl:when test="$to/@hangText">
+          <xsl:value-of select="normalize-space($to/@hangText)"/>
+        </xsl:when>
+        <xsl:when test="$to/@title">
+          <xsl:value-of select="normalize-space($to/@title)"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$to/@anchor"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="pn" select="normalize-space(substring-after($tcnt,'p.'))"/>
+      <xsl:text>Paragraph </xsl:text>
+      <xsl:choose>
+        <xsl:when test="$pn=''">
+          <xsl:text>?</xsl:text>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg" select="concat('No paragraph number for link target ',$from/@target)"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise><xsl:value-of select="$pn"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="xref-to-paragraph">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+  <xsl:param name="anchor"/>
+
+  <a href="#{$anchor}">
+    <xsl:call-template name="xref-to-paragraph-text">
+      <xsl:with-param name="from" select="$from"/>
+      <xsl:with-param name="to" select="$to"/>
+    </xsl:call-template>
+  </a>
+</xsl:template>
+
+<!-- xref to comment -->
+<xsl:template name="xref-to-comment">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+
+  <xsl:call-template name="emit-link">
+    <xsl:with-param name="target" select="concat('#',$from/@target)"/>
+    <xsl:with-param name="text">
+      <xsl:variable name="name">
+        <xsl:for-each select="$to">
+          <xsl:call-template name="get-comment-name" />
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="$from/@format='counter'">
+          <xsl:call-template name="error">
+            <xsl:with-param name="inline">no</xsl:with-param>
+            <xsl:with-param name="msg">xref to cref with format='counter' not allowed</xsl:with-param>
+          </xsl:call-template>
+          <xsl:value-of select="$name" />
+        </xsl:when>
+        <xsl:when test="$from/@format='none'">
+          <!-- Nothing to do -->
+        </xsl:when>
+        <xsl:when test="$from/@format='title'">
+          <xsl:value-of select="$to/@anchor"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="normalize-space(concat('Comment ',$name))"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-link">
+  <xsl:param name="target"/>
+  <xsl:param name="id"/>
+  <xsl:param name="title"/>
+  <xsl:param name="citation-title"/>
+  <xsl:param name="index-item"/>
+  <xsl:param name="index-subitem"/>
+  <xsl:param name="text"/>
+  <xsl:param name="child-nodes"/>
+  
+  <xsl:if test="$text!='' and $child-nodes">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">emit-link called both with text and child-nodes</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  
+  <xsl:variable name="element">
+    <xsl:choose>
+      <xsl:when test="$target!=''">a</xsl:when>
+      <xsl:when test="$citation-title!=''">cite</xsl:when>
+      <xsl:when test="($id!='' and $xml2rfc-ext-include-references-in-index='yes') or $title!=''">span</xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <xsl:choose>
+    <xsl:when test="$element!=''">
+      <xsl:element name="{$element}">
+        <xsl:if test="$target!=''">
+          <xsl:attribute name="href"><xsl:value-of select="$target"/></xsl:attribute>
+        </xsl:if>
+        <xsl:if test="$element='cite' and $citation-title!=''">
+          <xsl:attribute name="title"><xsl:value-of select="$citation-title"/></xsl:attribute>
+        </xsl:if>
+        <xsl:if test="$id!='' and $xml2rfc-ext-include-references-in-index='yes'">
+          <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+        </xsl:if>
+        <xsl:if test="$title!=''">
+          <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+        </xsl:if>
+        <xsl:choose>
+          <xsl:when test="$element!='cite' and $citation-title!=''">
+            <cite title="{$citation-title}">
+              <xsl:choose>
+                <xsl:when test="$child-nodes">
+                  <xsl:apply-templates select="$child-nodes"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="$text"/>
+                </xsl:otherwise>
+              </xsl:choose>
+            </cite>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:choose>
+              <xsl:when test="$child-nodes">
+                <xsl:apply-templates select="$child-nodes"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$text"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:element>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:choose>
+        <xsl:when test="$child-nodes">
+          <xsl:apply-templates select="$child-nodes"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$text"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- xref to reference -->
+<xsl:template name="xref-to-reference">
+  <xsl:param name="from"/>
+  <xsl:param name="to"/>
+  <xsl:param name="id"/>
+
+  <xsl:variable name="front" select="$to/front[1]|document($to/x:source/@href)/rfc/front[1]"/>
+
+  <xsl:variable name="is-xref" select="$from/self::xref"/>
+
+  <xsl:variable name="sfmt">
+    <xsl:for-each select="$from">
+      <xsl:call-template name="get-section-xref-format">
+        <xsl:with-param name="default">
+          <xsl:choose>
+            <xsl:when test="ancestor::artwork or ancestor::sourcecode">comma</xsl:when>
+            <xsl:otherwise>of</xsl:otherwise>
+          </xsl:choose>
+        </xsl:with-param>
+      </xsl:call-template>
+    </xsl:for-each>
+  </xsl:variable>
+
+  <xsl:variable name="ssec">
+    <xsl:for-each select="$from">
+      <xsl:call-template name="get-section-xref-section"/>
+    </xsl:for-each>
+  </xsl:variable>
+
+  <!-- check normative/informative -->
+  <xsl:variable name="t-is-normative" select="$from/ancestor-or-self::*[@x:nrm][1]"/>
+  <xsl:variable name="is-normative" select="$t-is-normative/@x:nrm='true'"/>
+  <xsl:if test="count($to)=1 and $is-normative">
+    <xsl:variable name="t-r-is-normative" select="$to/ancestor-or-self::*[@x:nrm][1]"/>
+    <xsl:variable name="r-is-normative" select="$t-r-is-normative/@x:nrm='true'"/>
+    <xsl:if test="not($r-is-normative)">
+      <xsl:for-each select="$from">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('Potentially normative reference to ',$from/@target,' not referenced normatively')"/>
+        </xsl:call-template>
+      </xsl:for-each>
+    </xsl:if>
+  </xsl:if>
+
+  <xsl:variable name="href">
+    <xsl:call-template name="computed-target">
+      <xsl:with-param name="bib" select="$to"/>
+      <xsl:with-param name="ref" select="$from"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:variable name="tsec">
+    <xsl:choose>
+      <xsl:when test="starts-with($from/@x:rel,'#') and $ssec=''">
+        <xsl:call-template name="compute-section-number">
+          <xsl:with-param name="bib" select="$to"/>
+          <xsl:with-param name="ref" select="$from"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="$from/@x:rel and not(starts-with($from/@x:rel,'#')) and $ssec=''">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">x:rel attribute '<xsl:value-of select="$from/@x:rel"/>' in reference to <xsl:value-of select="$to/@anchor"/> is expected to start with '#'.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$ssec"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <xsl:variable name="sec">
+    <xsl:choose>
+      <xsl:when test="contains($tsec,'@')">"<xsl:value-of select="substring-after($tsec,'@')"/>"</xsl:when>
+      <xsl:otherwise><xsl:value-of select="$tsec"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="secterm">
+    <xsl:choose>
+      <!-- starts with letter or unnumbered? -->
+      <xsl:when test="translate(substring($sec,1,1),$ucase,'')='' or starts-with($tsec,'A@')">Appendix</xsl:when>
+      <xsl:otherwise>Section</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="title">
+    <xsl:choose>
+      <xsl:when test="starts-with($from/@x:rel,'#') and $ssec='' and $to/x:source/@href">
+        <xsl:variable name="extdoc" select="document($to/x:source/@href)"/>
+        <xsl:variable name="anch" select="substring-after($from//@x:rel,'#')"/>
+        <xsl:variable name="nodes" select="$extdoc//*[@anchor=$anch or x:anchor-alias/@value=$anch]"/>
+        <xsl:if test="not($nodes)">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">Anchor '<xsl:value-of select="substring-after($from//@x:rel,'#')"/>' not found in <xsl:value-of select="$to/x:source/@href"/>.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:for-each select="$nodes">
+          <xsl:value-of select="@title"/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise />
+    </xsl:choose>
+  </xsl:variable>
+
+  <!--
+  Formats:
+
+    parens  [XXXX] (Section SS)
+    comma   [XXXX], Section SS
+    of      Section SS of [XXXX]
+    sec     Section SS
+    number  SS
+  -->
+
+  <xsl:if test="$sfmt!='' and not($sfmt='of' or $sfmt='section' or $sfmt='bare' or $sfmt='parens' or $sfmt='comma')">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg" select="concat('unknown xref section format extension: ',$sfmt)"/>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$sec!=''">
+    <xsl:choose>
+      <xsl:when test="$sfmt='of'">
+        <xsl:call-template name="emit-link">
+          <xsl:with-param name="target" select="$href"/>
+          <xsl:with-param name="text" select="concat($secterm,' ',$sec)"/>
+          <xsl:with-param name="title" select="$title"/>
+        </xsl:call-template>
+        <xsl:text> of </xsl:text>
+      </xsl:when>
+      <xsl:when test="$sfmt='section'">
+        <xsl:call-template name="emit-link">
+          <xsl:with-param name="target" select="$href"/>
+          <xsl:with-param name="text" select="concat($secterm,' ',$sec)"/>
+          <xsl:with-param name="title" select="$title"/>
+          <xsl:with-param name="id">
+            <xsl:if test="$sfmt='section'">
+              <xsl:value-of select="$id"/>
+            </xsl:if>
+          </xsl:with-param>
+          <xsl:with-param name="index-item" select="$from/@target"/>
+          <xsl:with-param name="index-subitem" select="$sec"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="$sfmt='bare'">
+        <xsl:call-template name="emit-link">
+          <xsl:with-param name="target" select="$href"/>
+          <xsl:with-param name="text" select="$sec"/>
+          <xsl:with-param name="title" select="$title"/>
+          <xsl:with-param name="id" select="$id"/>
+          <xsl:with-param name="index-item" select="$from/@target"/>
+          <xsl:with-param name="index-subitem" select="$sec"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise />
+    </xsl:choose>
+  </xsl:if>
+
+  <xsl:if test="$sec='' or ($sfmt!='section' and $sfmt!='bare')">
+    <xsl:call-template name="emit-link">
+      <xsl:with-param name="target" select="concat('#',$from/@target)"/>
+      <xsl:with-param name="text">
+        <xsl:variable name="val">
+          <xsl:call-template name="reference-name">
+            <xsl:with-param name="node" select="$to" />
+          </xsl:call-template>
+        </xsl:variable>
+        <xsl:choose>
+          <xsl:when test="$is-xref and $from/@format='none'">
+            <!-- nothing to do here -->
+          </xsl:when>
+          <xsl:when test="$is-xref and $from/@format='counter'">
+            <xsl:call-template name="error">
+              <xsl:with-param name="inline">no</xsl:with-param>
+              <xsl:with-param name="msg">xref to reference with format='counter' not allowed</xsl:with-param>
+            </xsl:call-template>
+            <!-- remove brackets -->
+            <xsl:value-of select="substring($val,2,string-length($val)-2)"/>
+          </xsl:when>
+          <xsl:when test="$is-xref and $from/@format='title'">
+            <xsl:choose>
+              <xsl:when test="$to/self::referencegroup">
+                <xsl:value-of select="$to/@anchor"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:apply-templates select="$front[1]/title/node()" mode="get-text-content"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:if test="not($is-xref) and $from/@format">
+              <xsl:call-template name="warning">
+                <xsl:with-param name="msg">@format attribute is undefined for relref</xsl:with-param>
+              </xsl:call-template>
+            </xsl:if>
+            <xsl:value-of select="$val"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:with-param>
+      <xsl:with-param name="id" select="$id"/>
+      <xsl:with-param name="index-item" select="$from/@target"/>
+      <xsl:with-param name="index-subitem" select="$sec"/>
+      <xsl:with-param name="citation-title" select="normalize-space($front[1]/title)"/>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$sec!=''">
+    <xsl:choose>
+      <xsl:when test="$sfmt='parens'">
+        <xsl:text> (</xsl:text>
+        <xsl:call-template name="emit-link">
+          <xsl:with-param name="target" select="$href"/>
+          <xsl:with-param name="text" select="concat($secterm,' ',$sec)"/>
+          <xsl:with-param name="title" select="$title"/>
+        </xsl:call-template>
+        <xsl:text>)</xsl:text>
+      </xsl:when>
+      <xsl:when test="$sfmt='comma'">
+        <xsl:text>, </xsl:text>
+        <xsl:call-template name="emit-link">
+          <xsl:with-param name="target" select="$href"/>
+          <xsl:with-param name="text" select="concat($secterm,' ',$sec)"/>
+          <xsl:with-param name="title" select="$title"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:if>
+  
+</xsl:template>
+
+<xsl:template name="get-target-anchor">
+  <xsl:variable name="xref" select="."/>
+  <xsl:for-each select="$src">
+    <xsl:variable name="tn" select="key('anchor-item',$xref/@target)|exslt:node-set($includeDirectives)//reference[@anchor=$xref/@target]"/>
+    <xsl:for-each select="$src">
+      <xsl:choose>
+        <xsl:when test="$tn/parent::artset and $tn/../@anchor">
+          <xsl:value-of select="$tn/../@anchor"/>
+        </xsl:when>
+        <xsl:when test="$tn/parent::artset and $tn/../artwork/@anchor">
+          <xsl:value-of select="$tn/../artwork[@anchor][1]/@anchor"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$xref/@target"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="xref[not(*|text())]|relref[not(*|text())]">
+
+  <xsl:variable name="xref" select="."/>
+
+  <xsl:variable name="target">
+    <xsl:call-template name="get-target-anchor"/>
+  </xsl:variable>
+
+  <xsl:if test="@target!=$target">
+    <xsl:call-template name="info">
+      <xsl:with-param name="msg">Target '<xsl:value-of select="@target"/>' rewritten to '<xsl:value-of select="$target"/>'.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="anchor"><xsl:value-of select="$anchor-pref"/>xref.<xsl:value-of select="$target"/>.<xsl:number level="any" count="xref[@target=$target]|relref[@target=$target]"/></xsl:variable>
+  
+  <!-- ensure we have the right context, this <xref> may be processed from within the boilerplate -->
+  <xsl:for-each select="$src">
+
+    <xsl:variable name="node" select="key('anchor-item',$target)|exslt:node-set($includeDirectives)//*[self::reference or self::referencegroup][@anchor=$target]"/>
+    <xsl:if test="count($node)=0 and not($node/ancestor::ed:del)">
+      <xsl:for-each select="$xref">
+        <xsl:choose>
+          <xsl:when test="not($xref/@target)">
+            <xsl:variable name="present">
+              <xsl:for-each select="$xref/@*">
+                <xsl:text> @</xsl:text>
+                <xsl:value-of select="local-name(.)"/>
+              </xsl:for-each>
+            </xsl:variable>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">Undefined target: no @target attribute specified<xsl:if test="$present!=''"> (attributes found:<xsl:value-of select="$present"/>)</xsl:if></xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg">Undefined target: '<xsl:value-of select="$xref/@target"/>'</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:if>
+
+    <xsl:choose>
+
+      <!-- Section links -->
+      <xsl:when test="$node/self::section or $node/self::appendix or $node/self::references or $node/self::abstract or $node/self::note">
+        <!-- index links to this xref -->
+        <xsl:variable name="ireftargets" select="key('iref-xanch',$target) | key('iref-xanch','')[../@anchor=$target]"/>
+        
+        <xsl:call-template name="xref-to-section">
+          <xsl:with-param name="from" select="$xref"/>
+          <xsl:with-param name="to" select="$node"/>
+          <xsl:with-param name="id" select="$anchor"/>
+          <xsl:with-param name="irefs" select="$ireftargets"/>
+        </xsl:call-template>
+      </xsl:when>
+
+      <!-- Figure links -->
+      <xsl:when test="$node/self::figure">
+        <xsl:call-template name="xref-to-figure">
+          <xsl:with-param name="from" select="$xref"/>
+          <xsl:with-param name="to" select="$node"/>
+        </xsl:call-template>
+      </xsl:when>
+
+      <!-- Table links -->
+      <xsl:when test="$node/self::texttable or $node/self::table">
+        <xsl:call-template name="xref-to-table">
+          <xsl:with-param name="from" select="$xref"/>
+          <xsl:with-param name="to" select="$node"/>
+        </xsl:call-template>
+      </xsl:when>
+
+      <!-- Paragraph links -->
+      <xsl:when test="$node/self::t or $node/self::aside or $node/self::x:note or $node/self::blockquote or $node/self::x:blockquote or $node/self::dl or $node/self::ol or $node/self::ul or $node/self::dd or $node/self::dt or $node/self::li or $node/self::artwork or $node/self::sourcecode or $node/self::artset">
+        <xsl:call-template name="xref-to-paragraph">
+          <xsl:with-param name="from" select="$xref"/>
+          <xsl:with-param name="to" select="$node"/>
+          <xsl:with-param name="anchor" select="$target"/>
+        </xsl:call-template>
+      </xsl:when>
+
+      <!-- Comment links -->
+      <xsl:when test="$node/self::cref">
+        <xsl:choose>
+          <xsl:when test="$node/@display='false'">
+            <xsl:for-each select="$xref">
+              <xsl:call-template name="error">
+                <xsl:with-param name="msg" select="concat('Comment ',$node/@anchor,' is hidden and thus can not be referenced')"/>
+              </xsl:call-template>
+            </xsl:for-each>
+          </xsl:when>
+          <xsl:when test="$xml2rfc-comments!='no'">
+            <xsl:call-template name="xref-to-comment">
+              <xsl:with-param name="from" select="$xref"/>
+              <xsl:with-param name="to" select="$node"/>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:for-each select="$xref">
+              <xsl:call-template name="error">
+                <xsl:with-param name="msg">xref to cref, but comments aren't included in the output</xsl:with-param>
+              </xsl:call-template>
+            </xsl:for-each>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+
+      <!-- Reference links -->
+      <xsl:when test="$node/self::reference or $node/self::referencegroup">
+        <xsl:call-template name="xref-to-reference">
+          <xsl:with-param name="from" select="$xref"/>
+          <xsl:with-param name="to" select="$node"/>
+          <xsl:with-param name="id" select="$anchor"/>
+        </xsl:call-template>
+      </xsl:when>
+
+      <xsl:otherwise>
+        <xsl:if test="$node">
+          <!-- make it the correct context -->
+          <xsl:for-each select="$xref">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="concat('xref to unknown element: ',name($node))"/>
+            </xsl:call-template>
+          </xsl:for-each>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+
+<!-- mark unmatched elements red -->
+
+<xsl:template match="*">
+  <xsl:call-template name="error">
+    <xsl:with-param name="inline" select="'no'"/>
+    <xsl:with-param name="msg">no XSLT template for element '<xsl:value-of select="name()"/>'</xsl:with-param>
+  </xsl:call-template>
+  <span class="tt {$css-error}">&lt;<xsl:value-of select="name()" />&gt;</span>
+  <xsl:copy><xsl:apply-templates select="node()|@*" /></xsl:copy>
+  <span class="tt {$css-error}">&lt;/<xsl:value-of select="name()" />&gt;</span>
+</xsl:template>
+
+<xsl:template match="/">
+  <xsl:apply-templates select="*" mode="validate"/>
+  <xsl:apply-templates select="*" />
+</xsl:template>
+
+<!-- utility templates -->
+
+<xsl:template name="collectLeftHeaderColumn">
+  <!-- default case -->
+  <xsl:if test="$xml2rfc-private=''">
+    <xsl:if test="count(/rfc/front/workgroup)>1">
+      <xsl:call-template name="error">
+        <xsl:with-param name="inline">no</xsl:with-param>
+        <xsl:with-param name="msg">There are multiple /rfc/front/workgroup elements; ignoring all but the first</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:for-each select="/rfc/front/workgroup">
+      <xsl:variable name="v" select="normalize-space(.)"/>
+      <xsl:if test="translate($v, $ucase, $lcase)='internet engineering task force' or $v=''">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">'<xsl:value-of select="$v"/>' definitively is not the name of a Working Group</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:for-each>
+    <xsl:choose>
+      <xsl:when test="$is-rfc and $header-format='2010' and $submissionType='independent'">
+        <myns:item>Independent Submission</myns:item>
+      </xsl:when>
+      <xsl:when test="$is-rfc and $header-format='2010' and $submissionType='IETF'">
+        <myns:item>Internet Engineering Task Force (IETF)</myns:item>
+      </xsl:when>
+      <xsl:when test="$is-rfc and $header-format='2010' and $submissionType='IRTF'">
+        <myns:item>Internet Research Task Force (IRTF)</myns:item>
+      </xsl:when>
+      <xsl:when test="$is-rfc and $header-format='2010' and $submissionType='IAB'">
+        <myns:item>Internet Architecture Board (IAB)</myns:item>
+      </xsl:when>
+      <xsl:when test="/rfc/front/workgroup and (not($is-rfc) or $rfcno='')">
+        <xsl:choose>
+          <xsl:when test="starts-with(/rfc/@docName,'draft-ietf-') and $submissionType='IETF'"/>
+          <xsl:when test="starts-with(/rfc/@docName,'draft-irft-') and $submissionType='IRTF'"/>
+          <xsl:otherwise>
+            <xsl:call-template name="info">
+              <xsl:with-param name="msg">The /rfc/front/workgroup should only be used for Working/Research Group drafts</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:for-each select="/rfc/front/workgroup">
+          <xsl:variable name="v" select="normalize-space(.)"/>
+          <xsl:variable name="tmp" select="translate($v, $ucase, $lcase)"/>
+          <xsl:if test="contains($tmp,' research group') or contains($tmp,' working group')">
+            <xsl:call-template name="info">
+              <xsl:with-param name="msg">No need to include 'Working Group' or 'Research Group' postfix in /rfc/front/workgroup value '<xsl:value-of select="$v"/>'</xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+          <xsl:variable name="h">
+            <!-- when a single name, append WG/RG postfix automatically -->
+            <xsl:choose>
+              <xsl:when test="not(contains($v, ' ')) and starts-with(/rfc/@docName,'draft-ietf-') and $submissionType='IETF'">
+                <xsl:value-of select="concat($v, ' Working Group')"/>
+              </xsl:when>
+              <xsl:when test="not(contains($v, ' ')) and starts-with(/rfc/@docName,'draft-irtf-') and $submissionType='IRTF'">
+                <xsl:value-of select="concat($v, ' Research Group')"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$v"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <myns:item>
+            <xsl:value-of select="$h"/>
+          </myns:item>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:if test="starts-with(/rfc/@docName,'draft-ietf-') and not(/rfc/front/workgroup)">
+          <xsl:call-template name="info">
+            <xsl:with-param name="msg">WG submissions should include a /rfc/front/workgroup element</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <myns:item>Network Working Group</myns:item>
+      </xsl:otherwise>
+    </xsl:choose>
+    <!-- check <area> value -->
+    <xsl:for-each select="/rfc/front/area">
+      <xsl:variable name="area" select="normalize-space(.)"/>
+      <xsl:variable name="rallowed">
+        <xsl:if test="$pub-yearmonth &lt; 201509">
+          <ed:v>Applications</ed:v>
+          <ed:v>app</ed:v>
+        </xsl:if>
+        <xsl:if test="$pub-yearmonth &gt; 201505">
+          <ed:v>Applications and Real-Time</ed:v>
+          <ed:v>art</ed:v>
+        </xsl:if>
+        <ed:v>General</ed:v>
+        <ed:v>gen</ed:v>
+        <ed:v>Internet</ed:v>
+        <ed:v>int</ed:v>
+        <ed:v>Operations and Management</ed:v>
+        <ed:v>ops</ed:v>
+        <xsl:if test="$pub-yearmonth &lt; 201509">
+          <ed:v>Real-time Applications and Infrastructure</ed:v>
+          <ed:v>rai</ed:v>
+        </xsl:if>
+        <ed:v>Routing</ed:v>
+        <ed:v>rtg</ed:v>
+        <ed:v>Security</ed:v>
+        <ed:v>sec</ed:v>
+        <ed:v>Transport</ed:v>
+        <ed:v>tsv</ed:v>
+      </xsl:variable>
+      <xsl:variable name="allowed" select="exslt:node-set($rallowed)"/>
+      <xsl:choose>
+        <xsl:when test="$allowed/ed:v=$area">
+          <!-- ok -->
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">Unknown IETF area: "<xsl:value-of select="$area"/>" - should be one of: <xsl:for-each select="$allowed/ed:v">
+              <xsl:text>"</xsl:text>
+              <xsl:value-of select="."/>
+              <xsl:text>"</xsl:text>
+              <xsl:if test="position()!=last()">
+                <xsl:text>, </xsl:text>
+              </xsl:if>
+            </xsl:for-each>
+            <xsl:text> (as of the publication date of </xsl:text>
+            <xsl:value-of select="$pub-yearmonth"/>
+            <xsl:text>)</xsl:text>
+            </xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+    <myns:item>
+       <xsl:choose>
+        <xsl:when test="/rfc/@ipr and not($is-rfc)">Internet-Draft</xsl:when>
+        <xsl:otherwise>
+          <xsl:text>Request for Comments: </xsl:text>
+          <xsl:value-of select="$rfcno"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </myns:item>
+    <xsl:if test="/rfc/@obsoletes!=''">
+      <myns:item>
+        <xsl:text>Obsoletes: </xsl:text>
+        <xsl:call-template name="rfclist">
+          <xsl:with-param name="list" select="normalize-space(/rfc/@obsoletes)" />
+        </xsl:call-template>
+        <xsl:if test="not($is-rfc)"> (if approved)</xsl:if>
+      </myns:item>
+    </xsl:if>
+    <xsl:if test="/rfc/@seriesNo">
+       <myns:item>
+        <xsl:choose>
+          <xsl:when test="/rfc/@category='bcp'">
+            <xsl:text>BCP: </xsl:text>
+            <xsl:value-of select="/rfc/@seriesNo"/>
+            <xsl:for-each select="/rfc/front/seriesInfo[@name='BCP']">
+              <xsl:if test="number(@value) != number(/rfc/@seriesNo)">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg">BCP number given in /rfc/front/seriesInfo (<xsl:value-of select="@value"/>) inconsistent with rfc element (<xsl:value-of select="/rfc/@seriesNo"/>)</xsl:with-param>
+                </xsl:call-template>
+              </xsl:if>
+            </xsl:for-each>
+          </xsl:when>
+          <xsl:when test="/rfc/@category='info'">
+            <xsl:text>FYI: </xsl:text>
+            <xsl:value-of select="/rfc/@seriesNo"/>
+            <xsl:for-each select="/rfc/front/seriesInfo[@name='FYI']">
+              <xsl:if test="number(@value) != number(/rfc/@seriesNo)">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg">FYI number given in /rfc/front/seriesInfo (<xsl:value-of select="@value"/>) inconsistent with rfc element (<xsl:value-of select="/rfc/@seriesNo"/>)</xsl:with-param>
+                </xsl:call-template>
+              </xsl:if>
+            </xsl:for-each>
+          </xsl:when>
+          <xsl:when test="/rfc/@category='std'">
+            <xsl:text>STD: </xsl:text>
+            <xsl:value-of select="/rfc/@seriesNo"/>
+            <xsl:for-each select="/rfc/front/seriesInfo[@name='STD']">
+              <xsl:if test="number(@value) != number(/rfc/@seriesNo)">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg">STD number given in /rfc/front/seriesInfo (<xsl:value-of select="@value"/>) inconsistent with rfc element (<xsl:value-of select="/rfc/@seriesNo"/>)</xsl:with-param>
+                </xsl:call-template>
+              </xsl:if>
+            </xsl:for-each>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">There is no IETF document series called '<xsl:value-of select="/rfc/@category"/>'</xsl:with-param>
+            </xsl:call-template>
+            <xsl:value-of select="concat(translate(/rfc/@category,$lcase,$ucase),': ',/rfc/@seriesNo)" />
+          </xsl:otherwise>
+        </xsl:choose>
+      </myns:item>
+    </xsl:if>
+    <xsl:if test="/rfc/@updates!=''">
+      <myns:item>
+        <xsl:text>Updates: </xsl:text>
+          <xsl:call-template name="rfclist">
+             <xsl:with-param name="list" select="normalize-space(/rfc/@updates)" />
+          </xsl:call-template>
+          <xsl:if test="not($is-rfc)"> (if approved)</xsl:if>
+      </myns:item>
+    </xsl:if>
+    <myns:item>
+      <xsl:choose>
+        <xsl:when test="$is-rfc">
+          <xsl:text>Category: </xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:text>Intended status: </xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:call-template name="get-category-long" />
+    </myns:item>
+    <xsl:if test="/rfc/@ipr and not($is-rfc)">
+       <myns:item>Expires: <xsl:call-template name="expirydate" /></myns:item>
+    </xsl:if>
+  </xsl:if>
+
+  <!-- private case -->
+  <xsl:if test="$xml2rfc-private!=''">
+    <myns:item><xsl:value-of select="$xml2rfc-private" /></myns:item>
+  </xsl:if>
+
+  <xsl:if test="$header-format='2010' and $is-rfc">
+    <myns:item>ISSN: 2070-1721</myns:item>
+  </xsl:if>
+</xsl:template>
+
+<!-- author name handling -->
+
+<xsl:template name="get-surname-from-fullname">
+  <xsl:param name="s"/>
+  <xsl:variable name="n" select="normalize-space($s)"/>
+  <xsl:choose>
+    <xsl:when test="contains($n,' ')">
+      <xsl:call-template name="get-surname-from-fullname">
+        <xsl:with-param name="s" select="substring-after($n,' ')"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$n"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-author-surname">
+  <xsl:variable name="s" select="normalize-space(@surname)"/>
+  <xsl:choose>
+    <xsl:when test="$s='' and normalize-space(@fullname)!=''">
+      <xsl:variable name="computed">
+        <xsl:call-template name="get-surname-from-fullname">
+          <xsl:with-param name="s" select="@fullname"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:call-template name="info">
+        <xsl:with-param name="msg">author/@surname is missing for author with fullname '<xsl:value-of select="@fullname"/>', extracted as '<xsl:value-of select="normalize-space($computed)"/>'</xsl:with-param>
+      </xsl:call-template>
+      <xsl:value-of select="normalize-space($computed)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$s"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-initials-from-fullname">
+  <xsl:param name="s"/>
+  <xsl:variable name="n" select="normalize-space($s)"/>
+  <xsl:choose>
+    <xsl:when test="contains($n,' ')">
+      <xsl:value-of select="substring($n,1,1)"/><xsl:text>. </xsl:text>
+      <xsl:call-template name="get-initials-from-fullname">
+        <xsl:with-param name="s" select="substring-after($n,' ')"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-author-initials">
+  <xsl:variable name="s" select="normalize-space(@initials)"/>
+  <xsl:choose>
+    <xsl:when test="$s='' and normalize-space(@fullname)!='' and normalize-space(@surname)=''">
+      <xsl:variable name="computed">
+        <xsl:call-template name="get-initials-from-fullname">
+          <xsl:with-param name="s" select="@fullname"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:call-template name="info">
+        <xsl:with-param name="msg">author/@initials is missing for author with fullname '<xsl:value-of select="@fullname"/>', extracted as '<xsl:value-of select="normalize-space($computed)"/>'</xsl:with-param>
+      </xsl:call-template>
+      <xsl:value-of select="normalize-space($computed)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$s"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="collectRightHeaderColumn">
+  <xsl:for-each select="author">
+    <xsl:variable name="surname">
+      <xsl:call-template name="get-author-surname"/>
+    </xsl:variable>
+    <xsl:variable name="rawinitials">
+      <xsl:call-template name="get-author-initials"/>
+    </xsl:variable>
+    <xsl:variable name="initials">
+      <xsl:call-template name="format-initials">
+        <xsl:with-param name="initials" select="$rawinitials"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="truncated-initials">
+      <xsl:call-template name="truncate-initials">
+        <xsl:with-param name="initials" select="$initials"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:if test="$surname!=''">
+      <myns:item>
+        <xsl:value-of select="$truncated-initials"/>
+        <xsl:if test="$truncated-initials!=''">
+          <xsl:text> </xsl:text>
+        </xsl:if>
+        <xsl:value-of select="$surname" />
+        <xsl:if test="@asciiInitials!='' or @asciiSurname!=''">
+          <xsl:text> (</xsl:text>
+            <xsl:value-of select="@asciiInitials"/>
+            <xsl:if test="@asciiInitials!='' and @asciiSurname!=''"> </xsl:if>
+            <xsl:value-of select="@asciiSurname"/>
+          <xsl:text>)</xsl:text>
+        </xsl:if>
+        <xsl:if test="@role">
+          <xsl:choose>
+            <xsl:when test="@role='editor'">
+              <xsl:text>, Editor</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:text>, </xsl:text><xsl:value-of select="@role" />
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+      </myns:item>
+    </xsl:if>
+    <xsl:variable name="org">
+      <xsl:choose>
+        <xsl:when test="organization/@showOnFrontPage='false'"/>
+        <xsl:when test="organization/@abbrev"><xsl:value-of select="organization/@abbrev"/></xsl:when>
+        <xsl:otherwise><xsl:value-of select="organization"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="orgOfFollowing">
+      <xsl:choose>
+        <xsl:when test="following-sibling::*[1]/organization/@showOnFrontPage='false'"/>
+        <xsl:when test="following-sibling::*[1]/organization/@abbrev"><xsl:value-of select="following-sibling::*[1]/organization/@abbrev" /></xsl:when>
+        <xsl:otherwise><xsl:value-of select="following-sibling::*/organization" /></xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$org != $orgOfFollowing and $org != ''">
+      <myns:item>
+        <xsl:value-of select="$org"/>
+        <xsl:if test="organization/@ascii">
+          <xsl:value-of select="concat(' (',organization/@ascii,')')"/>
+        </xsl:if>
+      </myns:item>
+    </xsl:if>
+  </xsl:for-each>
+  <myns:item>
+    <xsl:if test="$xml2rfc-ext-pub-month!=''">
+      <xsl:if test="$xml2rfc-ext-pub-day!='' and /rfc/front/date/@x:include-day='true' and $is-rfc">
+        <xsl:value-of select="number($xml2rfc-ext-pub-day)" />
+        <xsl:text> </xsl:text>
+      </xsl:if>
+      <xsl:value-of select="$xml2rfc-ext-pub-month" />
+      <xsl:if test="$xml2rfc-ext-pub-day!='' and /rfc/@ipr and not($is-rfc)">
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="number($xml2rfc-ext-pub-day)" />
+        <xsl:text>,</xsl:text>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="$xml2rfc-ext-pub-month='' and $rfcno!=''">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'month missing but is required for RFCs'"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$xml2rfc-ext-pub-day='' and /rfc/@docName and $rfcno='' and not(substring(/rfc/@docName, string-length(/rfc/@docName) - string-length('-latest') + 1) = '-latest')">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('/rfc/front/date/@day appears to be missing for a historic draft dated ', $pub-yearmonth)"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:value-of select="concat(' ',$xml2rfc-ext-pub-year)" />
+  </myns:item>
+</xsl:template>
+
+
+<xsl:template name="emitheader">
+  <xsl:param name="lc" />
+  <xsl:param name="rc" />
+
+  <tbody>
+    <xsl:for-each select="$lc/myns:item | $rc/myns:item">
+      <xsl:variable name="pos" select="position()" />
+      <xsl:if test="$pos &lt; count($lc/myns:item) + 1 or $pos &lt; count($rc/myns:item) + 1">
+        <tr>
+          <td class="{$css-left}"><xsl:call-template name="copynodes"><xsl:with-param name="nodes" select="$lc/myns:item[$pos]/node()" /></xsl:call-template></td>
+          <td class="{$css-right}"><xsl:call-template name="copynodes"><xsl:with-param name="nodes" select="$rc/myns:item[$pos]/node()" /></xsl:call-template></td>
+        </tr>
+      </xsl:if>
+    </xsl:for-each>
+  </tbody>
+</xsl:template>
+
+<!-- convenience template that avoids copying namespace nodes we don't want -->
+<xsl:template name="copynodes">
+  <xsl:param name="nodes" />
+  <xsl:for-each select="$nodes">
+    <xsl:choose>
+      <xsl:when test="namespace-uri()='http://www.w3.org/1999/xhtml'">
+        <xsl:element name="{name()}" namespace="{namespace-uri()}">
+          <xsl:copy-of select="@*|node()" />
+        </xsl:element>
+      </xsl:when>
+      <xsl:when test="self::*">
+        <xsl:element name="{name()}">
+          <xsl:copy-of select="@*|node()" />
+        </xsl:element>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:copy-of select="." />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="expirydate">
+  <xsl:param name="in-prose"/>
+  <xsl:choose>
+    <xsl:when test="number($xml2rfc-ext-pub-day) >= 1">
+      <!-- have day of month? -->
+      <xsl:if test="$in-prose">
+        <xsl:text>on </xsl:text>
+      </xsl:if>
+      <xsl:call-template name="normalize-date">
+        <xsl:with-param name="year" select="$xml2rfc-ext-pub-year"/>
+        <xsl:with-param name="month" select="$pub-month-numeric"/>
+        <xsl:with-param name="day" select="$xml2rfc-ext-pub-day + 185"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:if test="$in-prose">
+        <xsl:text>in </xsl:text>
+      </xsl:if>
+      <xsl:variable name="month">
+        <xsl:call-template name="get-month-as-num">
+          <xsl:with-param name="month" select="$xml2rfc-ext-pub-month"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="string(number($month))!='NaN' and number($month) &gt; 0 and number($month) &lt; 7">
+          <xsl:call-template name="get-month-as-name">
+            <xsl:with-param name="month" select="number($month) + 6"/>
+          </xsl:call-template>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-ext-pub-year" />
+        </xsl:when>
+        <xsl:when test="string(number($month))!='NaN' and number($month) &gt; 6 and number($month) &lt; 13">
+          <xsl:call-template name="get-month-as-name">
+            <xsl:with-param name="month" select="number($month) - 6"/>
+          </xsl:call-template>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-ext-pub-year + 1" />
+        </xsl:when>
+        <xsl:otherwise>WRONG SYNTAX FOR MONTH</xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="normalize-date">
+  <xsl:param name="year"/>
+  <xsl:param name="month"/>
+  <xsl:param name="day"/>
+
+  <xsl:variable name="isleap" select="(($year mod 4) = 0 and ($year mod 100 != 0)) or ($year mod 400) = 0" />
+
+  <!--<xsl:message>
+    <xsl:value-of select="concat($year,' ',$month,' ',$day)"/>
+  </xsl:message>-->
+
+  <xsl:variable name="dim">
+    <xsl:choose>
+      <xsl:when test="$month=1 or $month=3 or $month=5 or $month=7 or $month=8 or $month=10 or $month=12">31</xsl:when>
+      <xsl:when test="$month=2 and $isleap">29</xsl:when>
+      <xsl:when test="$month=2 and not($isleap)">28</xsl:when>
+      <xsl:otherwise>30</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$day > $dim and $month=12">
+      <xsl:call-template name="normalize-date">
+        <xsl:with-param name="year" select="$year + 1"/>
+        <xsl:with-param name="month" select="1"/>
+        <xsl:with-param name="day" select="$day - $dim"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="$day > $dim">
+      <xsl:call-template name="normalize-date">
+        <xsl:with-param name="year" select="$year"/>
+        <xsl:with-param name="month" select="$month + 1"/>
+        <xsl:with-param name="day" select="$day - $dim"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="get-month-as-name">
+        <xsl:with-param name="month" select="$month"/>
+      </xsl:call-template>
+      <xsl:value-of select="concat(' ',$day,', ',$year)"/>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="get-month-as-num">
+  <xsl:param name="month" />
+  <xsl:choose>
+    <xsl:when test="$month='January'">01</xsl:when>
+    <xsl:when test="$month='February'">02</xsl:when>
+    <xsl:when test="$month='March'">03</xsl:when>
+    <xsl:when test="$month='April'">04</xsl:when>
+    <xsl:when test="$month='May'">05</xsl:when>
+    <xsl:when test="$month='June'">06</xsl:when>
+    <xsl:when test="$month='July'">07</xsl:when>
+    <xsl:when test="$month='August'">08</xsl:when>
+    <xsl:when test="$month='September'">09</xsl:when>
+    <xsl:when test="$month='October'">10</xsl:when>
+    <xsl:when test="$month='November'">11</xsl:when>
+    <xsl:when test="$month='December'">12</xsl:when>
+    <xsl:otherwise>WRONG SYNTAX FOR MONTH</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-month-as-name">
+  <xsl:param name="month"/>
+  <xsl:choose>
+    <xsl:when test="$month=1">January</xsl:when>
+    <xsl:when test="$month=2">February</xsl:when>
+    <xsl:when test="$month=3">March</xsl:when>
+    <xsl:when test="$month=4">April</xsl:when>
+    <xsl:when test="$month=5">May</xsl:when>
+    <xsl:when test="$month=6">June</xsl:when>
+    <xsl:when test="$month=7">July</xsl:when>
+    <xsl:when test="$month=8">August</xsl:when>
+    <xsl:when test="$month=9">September</xsl:when>
+    <xsl:when test="$month=10">October</xsl:when>
+    <xsl:when test="$month=11">November</xsl:when>
+    <xsl:when test="$month=12">December</xsl:when>
+    <xsl:otherwise>WRONG SYNTAX FOR MONTH</xsl:otherwise>
+   </xsl:choose>
+</xsl:template>
+
+<!-- produce back section with author information -->
+<xsl:template name="get-authors-section-title">
+  <xsl:choose>
+    <xsl:when test="count(/rfc/front/author)=1">Author's Address</xsl:when>
+    <xsl:otherwise>Authors' Addresses</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-authors-section-number">
+  <xsl:if test="/*/x:assign-section-number[@builtin-target='authors']">
+    <xsl:value-of select="/*/x:assign-section-number[@builtin-target='authors']/@number"/>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insertAuthors">
+
+  <xsl:variable name="number">
+    <xsl:call-template name="get-authors-section-number"/>
+  </xsl:variable>
+
+  <xsl:if test="$number!='suppress' and $xml2rfc-authorship!='no'">
+    <xsl:call-template name="insert-conditional-hrule"/>
+
+    <section id="{$anchor-pref}authors" class="avoidbreakinside">
+      <xsl:call-template name="insert-conditional-pagebreak"/>
+      <h2>
+        <xsl:if test="$number != ''">
+          <a href="#{$anchor-pref}section.{$number}" id="{$anchor-pref}section.{$number}"><xsl:value-of select="$number"/>.</a>
+          <xsl:text> </xsl:text>
+        </xsl:if>
+        <a href="#{$anchor-pref}authors"><xsl:call-template name="get-authors-section-title"/></a>
+      </h2>
+
+      <xsl:apply-templates select="/rfc/front/author" />
+    </section>
+  </xsl:if>
+</xsl:template>
+
+
+
+<!-- insert copyright statement -->
+
+<xsl:template name="insertCopyright" myns:namespaceless-elements="xml2rfc">
+
+<boilerplate>
+  <xsl:if test="not($no-copylong)">
+    <section title="Full Copyright Statement" anchor="{$anchor-pref}copyright" x:fixed-section-number="3">
+      <xsl:choose>
+        <xsl:when test="$ipr-rfc3667">
+          <t>
+            <xsl:choose>
+              <xsl:when test="$ipr-rfc4748">
+                Copyright &#169; The IETF Trust (<xsl:value-of select="$xml2rfc-ext-pub-year" />).
+              </xsl:when>
+              <xsl:otherwise>
+                Copyright &#169; The Internet Society (<xsl:value-of select="$xml2rfc-ext-pub-year" />).
+              </xsl:otherwise>
+            </xsl:choose>
+          </t>
+          <t>
+            This document is subject to the rights, licenses and restrictions
+            contained in BCP 78<xsl:if test="$submissionType='independent'"> and at <eref target="http://www.rfc-editor.org/copyright.html">http://www.rfc-editor.org/copyright.html</eref></xsl:if>, and except as set forth therein, the authors
+            retain all their rights.
+          </t>
+          <t>
+            This document and the information contained herein are provided
+            on an &#8220;AS IS&#8221; basis and THE CONTRIBUTOR,
+            THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY),
+            THE INTERNET SOCIETY<xsl:if test="$ipr-rfc4748">, THE IETF TRUST</xsl:if>
+            AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES,
+            EXPRESS OR IMPLIED,
+            INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+            INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+            WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+          </t>
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- <http://tools.ietf.org/html/rfc2026#section-10.4> -->
+          <t>
+            Copyright &#169; The Internet Society (<xsl:value-of select="$xml2rfc-ext-pub-year" />). All Rights Reserved.
+          </t>
+          <t>
+            This document and translations of it may be copied and furnished to
+            others, and derivative works that comment on or otherwise explain it
+            or assist in its implementation may be prepared, copied, published and
+            distributed, in whole or in part, without restriction of any kind,
+            provided that the above copyright notice and this paragraph are
+            included on all such copies and derivative works. However, this
+            document itself may not be modified in any way, such as by removing
+            the copyright notice or references to the Internet Society or other
+            Internet organizations, except as needed for the purpose of
+            developing Internet standards in which case the procedures for
+            copyrights defined in the Internet Standards process must be
+            followed, or as required to translate it into languages other than
+            English.
+          </t>
+          <t>
+            The limited permissions granted above are perpetual and will not be
+            revoked by the Internet Society or its successors or assigns.
+          </t>
+          <t>
+            This document and the information contained herein is provided on an
+            &#8220;AS IS&#8221; basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+            TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+            BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+            HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+            MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+          </t>
+        </xsl:otherwise>
+      </xsl:choose>
+    </section>
+
+    <section title="Intellectual Property" anchor="{$anchor-pref}ipr" x:fixed-section-number="4">
+      <xsl:choose>
+        <xsl:when test="$ipr-rfc3667">
+          <t>
+            The IETF takes no position regarding the validity or scope of any
+            Intellectual Property Rights or other rights that might be claimed to
+            pertain to the implementation or use of the technology described in
+            this document or the extent to which any license under such rights
+            might or might not be available; nor does it represent that it has
+            made any independent effort to identify any such rights.  Information
+            on the procedures with respect to rights in RFC documents
+            can be found in BCP 78 and BCP 79.
+          </t>
+          <t>
+            Copies of IPR disclosures made to the IETF Secretariat and any
+            assurances of licenses to be made available, or the result of an
+            attempt made to obtain a general license or permission for the use
+            of such proprietary rights by implementers or users of this
+            specification can be obtained from the IETF on-line IPR repository
+            at <eref target="http://www.ietf.org/ipr">http://www.ietf.org/ipr</eref>.
+          </t>
+          <t>
+            The IETF invites any interested party to bring to its attention any
+            copyrights, patents or patent applications, or other proprietary
+            rights that may cover technology that may be required to implement
+            this standard. Please address the information to the IETF at
+            <eref target="mailto:ietf-ipr@ietf.org">ietf-ipr@ietf.org</eref>.
+          </t>
+        </xsl:when>
+        <xsl:otherwise>
+          <t>
+            The IETF takes no position regarding the validity or scope of
+            any intellectual property or other rights that might be claimed
+            to  pertain to the implementation or use of the technology
+            described in this document or the extent to which any license
+            under such rights might or might not be available; neither does
+            it represent that it has made any effort to identify any such
+            rights. Information on the IETF's procedures with respect to
+            rights in standards-track and standards-related documentation
+            can be found in BCP-11. Copies of claims of rights made
+            available for publication and any assurances of licenses to
+            be made available, or the result of an attempt made
+            to obtain a general license or permission for the use of such
+            proprietary rights by implementors or users of this
+            specification can be obtained from the IETF Secretariat.
+          </t>
+          <t>
+            The IETF invites any interested party to bring to its
+            attention any copyrights, patents or patent applications, or
+            other proprietary rights which may cover technology that may be
+            required to practice this standard. Please address the
+            information to the IETF Executive Director.
+          </t>
+          <xsl:if test="$xml2rfc-iprnotified='yes'">
+            <t>
+              The IETF has been notified of intellectual property rights
+              claimed in regard to some or all of the specification contained
+              in this document. For more information consult the online list
+              of claimed rights.
+            </t>
+          </xsl:if>
+        </xsl:otherwise>
+      </xsl:choose>
+    </section>
+
+    <xsl:choose>
+      <xsl:when test="$no-funding"/>
+      <xsl:when test="$funding1 and $is-rfc">
+        <section x:fixed-section-number="5">
+          <xsl:attribute name="title">
+            <xsl:choose>
+              <xsl:when test="$xml2rfc-rfcedstyle='yes'">Acknowledgement</xsl:when>
+              <xsl:otherwise>Acknowledgment</xsl:otherwise>
+            </xsl:choose>
+          </xsl:attribute>
+          <t>
+            Funding for the RFC Editor function is provided by the IETF
+            Administrative Support Activity (IASA).
+          </t>
+        </section>
+      </xsl:when>
+      <xsl:when test="$funding0 and $is-rfc">
+        <section x:fixed-section-number="5">
+          <xsl:attribute name="title">
+            <xsl:choose>
+              <xsl:when test="$xml2rfc-rfcedstyle='yes'">Acknowledgement</xsl:when>
+              <xsl:otherwise>Acknowledgment</xsl:otherwise>
+            </xsl:choose>
+          </xsl:attribute>
+          <t>
+            Funding for the RFC Editor function is currently provided by
+            the Internet Society.
+          </t>
+        </section>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:if>
+</boilerplate>
+</xsl:template>
+
+<!-- optional scripts -->
+<xsl:template name="insertScripts">
+<xsl:if test="$xml2rfc-ext-refresh-from!=''">
+<script>
+var RfcRefresh = {};
+RfcRefresh.NS_XHTML = "http://www.w3.org/1999/xhtml";
+RfcRefresh.NS_MOZERR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
+RfcRefresh.lastTxt = "";
+RfcRefresh.lastEtag = "";
+RfcRefresh.xslt = null;
+RfcRefresh.xmlsource = "<xsl:value-of select='$xml2rfc-ext-refresh-from'/>";
+RfcRefresh.xsltsource = "<xsl:value-of select='$xml2rfc-ext-refresh-xslt'/>";
+RfcRefresh.interval = "<xsl:value-of select='number($xml2rfc-ext-refresh-interval)'/>";
+
+RfcRefresh.getXSLT = function() {
+  if (! window.XSLTProcessor) {
+    var err = document.createElement("pre");
+    err.className = "refreshbrowsererror <xsl:value-of select="$css-noprint"/>";
+    var msg = "This browser does not support the window.XSLTProcessor functionality.";
+    err.appendChild(document.createTextNode(msg));
+    RfcRefresh.showMessage("refreshxmlerror", err);
+  }
+  else {
+    try {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", RfcRefresh.xsltsource, true);
+      xhr.onload = function (e) {
+        if (xhr.readyState === 4) {
+          RfcRefresh.xslt = new XSLTProcessor();
+          RfcRefresh.xslt.importStylesheet(xhr.responseXML);
+        }
+      }
+      xhr.onerror = function (e) {
+        console.error(xhr.status + " " + xhr.statusText);
+      };
+      xhr.send(null);
+    }
+    catch (e) {
+      var err = document.createElement("pre");
+      err.className = "refreshbrowsererror <xsl:value-of select="$css-noprint"/>";
+      var msg = "Failed to load XSLT code from &lt;" + RfcRefresh.xsltsource + "&gt;.\n";
+      msg += "Your browser might not support loading from a file: URI.\n";
+      msg += "Error details: " + e;
+      err.appendChild(document.createTextNode(msg));
+      RfcRefresh.showMessage("refreshxmlerror", err);
+    }
+  }
+}
+
+RfcRefresh.findAndUpdate = function(olddoc, elem) {
+  var changed = "";
+  var children = elem.childNodes;
+  for (var i = 0; i != children.length; i++) {
+    var n = children[i];
+    if (n.nodeType == 1) {
+      var c = RfcRefresh.findAndUpdate(olddoc, n);
+      if (changed == '') {
+        changed = c;
+      }
+      var id = n.id;
+      if (id != "") {
+        var old = olddoc.getElementById(id);
+        var newtext = n.innerHTML;
+        if (!old) {
+          console.debug("new " + id);
+        } else {
+          var oldtext = old.innerHTML;
+          if (oldtext != newtext) {
+            console.debug("updating " + id);
+            old.innerHTML = n.innerHTML;
+            if (changed == '') {
+              changed = id;
+            }
+          }
+        }
+      }
+    }
+  }
+  return changed;
+}
+
+RfcRefresh.findDifferences = function(olddoc, newdoc) {
+  var changed = RfcRefresh.findAndUpdate(olddoc, newdoc.documentElement);
+  if (changed != "") {
+    console.debug("changed: " + changed);
+    document.location = "#" + changed;
+  }
+  // final check for changes; if those were not processed earlier,
+  // we refresh the whole document
+  var oldtext = olddoc.documentElement.getElementsByTagName("body")[0].innerHTML;
+  var newtext = newdoc.documentElement.getElementsByTagName("body")[0].innerHTML;
+  if (oldtext != newtext) {
+    console.debug("full refresh: " + newtext);
+    olddoc.documentElement.innerHTML = newdoc.documentElement.innerHTML;
+  }
+}
+
+RfcRefresh.getNodeText = function(elem) {
+  var result = "";
+  var children = elem.childNodes;
+  for (var i = 0; i != children.length; i++) {
+    if (children[i].nodeType == 3) {
+      result += children[i].nodeValue;
+    }
+  }
+  return result; 
+}
+
+RfcRefresh.getParserError = function(dom) {
+  // FIREFOX
+  if ("parsererror" == dom.documentElement.nodeName &amp;&amp; RfcRefresh.NS_MOZERR == dom.documentElement.namespaceURI) {
+    var errmsg = new Object();
+    errmsg.msg = "";
+    errmsg.src = "";
+    var children = dom.documentElement.childNodes;
+    for (var i = 0; i != children.length; i++) {
+      if (children[i].nodeType == 3) {
+        errmsg.msg += children[i].nodeValue;
+      } else if (children[i].nodeName == "sourcetext") {
+        errmsg.src = RfcRefresh.getNodeText(children[i]);
+      }
+    }
+    return errmsg;
+  }
+  
+  var list = dom.getElementsByTagNameNS(RfcRefresh.NS_XHTML, "parsererror");
+  if (list.length != 0) {
+    // Webkit
+    var errmsg = new Object();
+    errmsg.msg = "XML parse error";
+    list = dom.getElementsByTagNameNS(RfcRefresh.NS_XHTML, "div");
+    if (list.length != 0) {
+      errmsg.msg = RfcRefresh.getNodeText(list[0]);
+    }
+    return errmsg;
+  }  
+  
+  
+  return null;
+}
+
+RfcRefresh.showMessage = function(cls, node) {
+  // remove previous message
+  var list = document.getElementsByClassName(cls);
+  if (list.length != 0) {
+    list[0].parentNode.removeChild(list[0]);
+  }
+  document.body.appendChild(node);
+}
+
+RfcRefresh.refresh = function(txt) {
+  if (txt != RfcRefresh.lastTxt) {
+    RfcRefresh.lastTxt = txt;
+    // try to parse
+    var parser = new DOMParser();
+    var dom = parser.parseFromString(txt, "text/xml");
+    var errmsg = RfcRefresh.getParserError(dom);
+    
+    if (errmsg != null) {
+      var err = document.createElement("pre");
+      err.className = "refreshxmlerror <xsl:value-of select="$css-noprint"/>";
+      err.appendChild(document.createTextNode(errmsg.msg));
+      if (errmsg.src != null) {
+        err.appendChild(document.createElement("hr"));
+        err.appendChild(document.createTextNode(errmsg.src));
+      }
+      RfcRefresh.showMessage("refreshxmlerror", err);
+    } else {
+      // find new refresh
+      var children = dom.childNodes;
+      for (var i = 0; i != children.length; i++) {
+        if (children[i].nodeType == 7 &amp;&amp; children[i].target == "rfc-ext") {
+          var s = "&lt;foo " + children[i].data + "/>";
+          var sd = parser.parseFromString(s, "text/xml");
+          var refresh = sd.documentElement.getAttribute("refresh-interval");
+          if (refresh != null &amp;&amp; refresh != "") {
+            refresh = parseInt(refresh, 10);
+            if (RfcRefresh.interval != refresh) {
+              if (Number.isNaN(refresh) || refresh &lt; 5) {
+                console.debug("refresh requested to be: " + refresh + " - ignored, using 5 instead.");
+                RfcRefresh.interval = 5;
+              } else {
+                RfcRefresh.interval = refresh;
+                console.debug("refresh changed to: " + refresh);
+              }
+            }
+          }
+        }
+      }
+    
+      var html = RfcRefresh.xslt.transformToDocument(dom);
+      RfcRefresh.findDifferences(document, html);
+    }
+  }
+}
+
+RfcRefresh.initRefresh = function() {
+  RfcRefresh.getXSLT();
+    
+  window.setTimeout(function(){
+    if (RfcRefresh.xslt != null) {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", RfcRefresh.xmlsource, true);
+      if (RfcRefresh.lastEtag != "") {
+        xhr.setRequestHeader("If-None-Match", RfcRefresh.lastEtag);
+      }
+      xhr.onload = function (e) {
+        if (xhr.readyState === 4) {
+          console.debug(xhr.status + " " + xhr.statusText);
+          if (xhr.status != 304) {
+            RfcRefresh.refresh(xhr.responseText);
+          }
+          RfcRefresh.lastEtag = xhr.getResponseHeader("ETag");
+        }
+      }
+      xhr.onerror = function (e) {
+        console.error(xhr.status + " " + xhr.statusText);
+      };
+      xhr.send(null);
+      setTimeout(arguments.callee, RfcRefresh.interval * 1000);
+    }
+  }, RfcRefresh.interval * 1000);
+}
+</script>
+</xsl:if>
+<xsl:if test="/rfc/x:feedback">
+<script>
+var buttonsAdded = false;
+
+function initFeedback() {
+  var fb = document.createElement("div");
+  fb.className = "<xsl:value-of select="concat($css-feedback,' ',$css-noprint)"/>";
+  fb.setAttribute("onclick", "feedback();");
+  fb.appendChild(document.createTextNode("feedback"));
+
+  document.body.appendChild(fb);
+}
+
+function feedback() {
+  toggleButtonsToElementsByName("h2");
+  toggleButtonsToElementsByName("h3");
+  toggleButtonsToElementsByName("h4");
+  toggleButtonsToElementsByName("h5");
+
+  buttonsAdded = !buttonsAdded;
+}
+
+function toggleButtonsToElementsByName(name) {
+  var list = document.getElementsByTagName(name);
+  for (var i = 0; i &lt; list.length; i++) {
+    toggleButton(list.item(i));
+  }
+}
+
+function toggleButton(node) {
+  if (! buttonsAdded) {
+
+    // docname
+    var template = "<xsl:call-template name="replace-substring">
+  <xsl:with-param name="string" select="/rfc/x:feedback/@template"/>
+  <xsl:with-param name="replace">"</xsl:with-param>
+  <xsl:with-param name="by">\"</xsl:with-param>
+</xsl:call-template>";
+
+    var id = node.getAttribute("id");
+    // try also parent
+    if (id == null || id == "") {
+      var id = node.parentNode.getAttribute("id");
+    }
+    // better id available?
+    var titlelinks = node.getElementsByTagName("a");
+    for (var i = 0; i &lt; titlelinks.length; i++) {
+      var tl = titlelinks.item(i);
+      if (tl.getAttribute("id")) {
+        id = tl.getAttribute("id");
+      }
+    }
+    
+    // ref
+    var ref = window.location.toString();
+    var hash = ref.indexOf("#");
+    if (hash != -1) {
+      ref = ref.substring(0, hash);
+    }
+    if (id != null &amp;&amp; id != "") {
+      ref += "#" + id;
+    }
+
+    // docname
+    var docname = "<xsl:value-of select="/rfc/@docName"/>";
+
+    // section
+    var section = node.textContent;
+    section = section.replace("\u00a0", " ").trim();
+
+    // build URI from template
+    var uri = template.replace("{docname}", encodeURIComponent(docname));
+    uri = uri.replace("{section}", encodeURIComponent(section));
+    uri = uri.replace("{ref}", encodeURIComponent(ref));
+
+    var button = document.createElement("a");
+    button.className = "<xsl:value-of select="concat($css-fbbutton,' ',$css-noprint)"/>";
+    button.setAttribute("href", uri);
+    button.appendChild(document.createTextNode("send feedback"));
+    node.appendChild(button);
+  }
+  else {
+    var buttons = node.getElementsByTagName("a");
+    for (var i = 0; i &lt; buttons.length; i++) {
+      var b = buttons.item(i);
+      if (b.className == "<xsl:value-of select="concat($css-fbbutton,' ',$css-noprint)"/>") {
+        node.removeChild(b);
+      }
+    }
+  }
+}</script></xsl:if>
+<xsl:if test="$xml2rfc-ext-insert-metadata='yes' and ($is-rfc or $is-submitted-draft)"><script>
+<xsl:if test="$rfcno!=''">
+function getMeta(rfcno, container) {
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", "https://www.rfc-editor.org/rfc/rfc" + rfcno + ".json", true);
+  xhr.onload = function (e) {
+    if (xhr.readyState === 4) {
+      if (xhr.status === 200) {
+        var data = JSON.parse(xhr.response);
+        
+        var cont = document.getElementById(container);
+        // empty the container
+        while (cont.firstChild) {
+          cont.removeChild(myNode.firstChild);
+        }
+
+        var c = data.status;
+        if (c) {
+          var bld = newElementWithText("b", c);
+          cont.appendChild(bld);
+        } else {
+          cont.appendChild(newElementWithText("i", "(document status unknown)"));
+        }
+
+        c = data.updated_by;
+        if (c &amp;&amp; c.length > 0 &amp;&amp; c[0] !== null &amp;&amp; c[0].length > 0) {
+          cont.appendChild(newElement("br"));
+          cont.appendChild(newText("Updated by: "));
+          appendRfcLinks(cont, c);
+        }
+
+        c = data.obsoleted_by;
+        if (c &amp;&amp; c.length > 0 &amp;&amp; c[0] !== null &amp;&amp; c[0].length > 0) {
+          cont.appendChild(newElement("br"));
+          cont.appendChild(newText("Obsoleted by: "));
+          appendRfcLinks(cont, c);
+        }
+
+        c = data.errata_url;
+        if (c) {
+          cont.appendChild(newElement("br"));
+          var link = newElementWithText("a", "errata");
+          link.setAttribute("href", c);
+          var errata = newElementWithText("i", "This document has ");
+          errata.appendChild(link);
+          errata.appendChild(newText("."));
+          cont.appendChild(errata);
+        }
+
+        cont.style.display = "block";
+      } else {
+        console.error(xhr.statusText);
+      }
+    }
+  };
+  xhr.onerror = function (e) {
+    console.error(xhr.status + " " + xhr.statusText);
+  };
+  xhr.send(null);
+}
+function appendRfcLinks(parent, updates) {
+  var template = "<xsl:call-template name="replace-substring">
+  <xsl:with-param name="string" select="$xml2rfc-ext-rfc-uri"/>
+  <xsl:with-param name="replace">"</xsl:with-param>
+  <xsl:with-param name="by">\"</xsl:with-param>
+</xsl:call-template>";
+  for (var i = 0; i &lt; updates.length; i++) {
+    var rfc = updates[i].trim().toLowerCase();
+    if (rfc.substring(0, 3) == "rfc") {
+      var no = parseInt(rfc.substring(3), 10);
+      
+      var link = newElement("a");
+      link.setAttribute("href", template.replace("{rfc}", no));
+      link.appendChild(newText(no));
+      parent.appendChild(link);
+    } else {
+      parent.appendChild(newText(rfc));
+    }
+    if (i != updates.length - 1) {
+      parent.appendChild(newText(", "));
+    }
+  }
+}</xsl:if><xsl:if test="$is-submitted-draft">
+function getMeta(docname, revision, container) {
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", "https://datatracker.ietf.org/doc/" + docname + "/doc.json", true);
+  xhr.onload = function (e) {
+    if (xhr.readyState === 4) {
+      if (xhr.status === 200) {
+        var data = JSON.parse(xhr.response);
+        
+        var cont = document.getElementById(container);
+        // empty the container
+        while (cont.firstChild) {
+          cont.removeChild(myNode.firstChild);
+        }
+
+        if (data.rev) {
+          cont.style.display = "block";
+          var bld = newElementWithText("b", "Internet Draft Status");
+          cont.appendChild(bld);
+          cont.appendChild(newElement("br"));
+          if (data.rev == revision) {
+            var rev = newElementWithText("i", "This is the latest submitted version.");
+            cont.appendChild(rev);
+          } else {
+            var rev = newElementWithText("i", "This is not the current version:");
+            cont.appendChild(rev);
+            cont.appendChild(newElement("br"));
+            var dat = "";
+            if (data.time) {
+              dat = ", submitted on " + data.time.substring(0,10);
+            }
+            rev = newElementWithText("i", "please see version " + data.rev + dat + ".");
+            cont.appendChild(rev);
+          }
+        }
+      } else {
+        console.error(xhr.statusText);
+      }
+    }
+  };
+  xhr.onerror = function (e) {
+    console.error(xhr.status + " " + xhr.statusText);
+  };
+  xhr.send(null);
+}</xsl:if>
+
+// DOM helpers
+function newElement(name) {
+  return document.createElement(name);
+}
+function newElementWithText(name, txt) {
+  var e = document.createElement(name);
+  e.appendChild(newText(txt));
+  return e;
+}
+function newText(text) {
+  return document.createTextNode(text);
+}
+</script>
+</xsl:if>
+<script>
+function anchorRewrite() {
+<xsl:text>  map = { </xsl:text>
+  <xsl:for-each select="//x:anchor-alias">
+    <xsl:text>"</xsl:text>
+    <xsl:call-template name="replace-substring">
+      <xsl:with-param name="string" select="@value"/>
+      <xsl:with-param name="replace">"</xsl:with-param>
+      <xsl:with-param name="by">\"</xsl:with-param>
+    </xsl:call-template>
+    <xsl:text>": "</xsl:text>
+    <xsl:call-template name="replace-substring">
+      <xsl:with-param name="string" select="ancestor::*[@anchor][1]/@anchor"/>
+      <xsl:with-param name="replace">"</xsl:with-param>
+      <xsl:with-param name="by">\"</xsl:with-param>
+    </xsl:call-template>
+    <xsl:text>"</xsl:text>
+    <xsl:if test="position()!=last()">, </xsl:if>
+  </xsl:for-each>
+<xsl:text>};</xsl:text>
+  if (window.location.hash.length >= 1) {
+    var fragid = window.location.hash.substr(1);
+    if (fragid) {
+      if (! document.getElementById(fragid)) {
+        var prefix = "<xsl:value-of select="$anchor-pref"/>";
+        var mapped = map[fragid];
+        if (mapped) {
+          window.location.hash = mapped;
+        } else if (fragid.indexOf("section-") == 0) {
+          window.location.hash = prefix + "section." + fragid.substring(8);
+        } else if (fragid.indexOf("appendix-") == 0) {
+          window.location.hash = prefix + "section." + fragid.substring(9);
+        } else if (fragid.indexOf("s-") == 0) {
+          var postfix = fragid.substring(2);
+          if (postfix.startsWith("abstract")) {
+            window.location.hash = prefix + postfix;
+          } else if (postfix.startsWith("note-")) {
+            window.location.hash = prefix + "note." + postfix.substring(5);
+          } else {
+            window.location.hash = prefix + "section." + postfix;
+          }
+        } else if (fragid.indexOf("p-") == 0) {
+          var r = fragid.substring(2);
+          var p = r.indexOf("-");
+          if (p >= 0) {
+            window.location.hash = prefix + "section." + r.substring(0, p) + ".p." + r.substring(p + 1);
+          }
+        }
+      }
+    }  
+  }
+}
+window.addEventListener('hashchange', anchorRewrite);
+window.addEventListener('DOMContentLoaded', anchorRewrite);
+</script><xsl:if test="$prettyprint-script!=''">
+<script src="{$prettyprint-script}"/></xsl:if><xsl:if test="contains($prettyprint-script,'prettify') and (//artwork[contains(@type,'abnf')] or //sourcecode[contains(@type,'abnf')])">
+<script><![CDATA[try {
+PR['registerLangHandler'](
+    PR['createSimpleLexer'](
+        [
+         // comment
+         [PR['PR_COMMENT'], /^;[^\x00-\x1f]*/, null, ";"],
+        ],
+        [
+         // string literals
+         [PR['PR_STRING'], /^(\%s|\%i)?"[^"\x00-\x1f]*"/, null],
+         // binary literals
+         [PR['PR_LITERAL'], /^\%b[01]+((-[01]+)|(\.[01]+)*)/, null],
+         // decimal literals
+         [PR['PR_LITERAL'], /^\%d[0-9]+((-[0-9]+)|(\.[0-9]+)*)/, null],
+         // hex literals
+         [PR['PR_LITERAL'], /^(\%x[A-Za-z0-9]+((-[A-Za-z0-9]+)|(\.[A-Za-z0-9]+)*))/, null],
+         // prose rule
+         [PR['PR_NOCODE'], /^<[^>\x00-\x1f]*>/, null],
+         // rule name
+         [PR['PR_TYPE'], /^([A-Za-z][A-Za-z0-9-]*)/, null],
+         [PR['PR_PUNCTUATION'], /^[=\(\)\*\/\[\]#]/, null],
+        ]),
+    ['ietf_abnf']);
+} catch(e){}]]>
+</script>
+</xsl:if></xsl:template>
+
+<!-- insert CSS style info -->
+
+<xsl:template name="insertCss">
+<style title="rfc2629.xslt">
+<xsl:value-of select="$xml2rfc-ext-webfonts"/>
+:root {
+  --col-bg: white;
+  --col-bg-error: red;
+  --col-bg-highlight: yellow;
+  --col-bg-highligh2: lime;
+  --col-bg-light: gray;
+  --col-bg-pre: lightyellow;
+  --col-bg-pre1: #f8f8f8;
+  --col-bg-pre2: #f0f0f0;
+  --col-bg-th: #e9e9e9;
+  --col-bg-tr: #f5f5f5;
+  --col-fg: black;
+  --col-fg-del: red;
+  --col-fg-error: red;
+  --col-fg-ins: green;
+  --col-fg-light: gray;
+  --col-fg-link: blue;
+  --col-fg-title: green;
+}
+a {
+  color: var(--col-fg-link);
+  text-decoration: none;
+}
+a.smpl {
+  color: var(--col-fg);
+}
+a:hover {
+  text-decoration: underline;
+}
+a:active {
+  text-decoration: underline;
+}
+address {
+  margin-top: 1em;
+  margin-left: 2em;
+  font-style: normal;
+}<xsl:if test="//x:blockquote|//blockquote">
+blockquote {
+  border-style: solid;
+  border-color: var(--col-fg-light);
+  border-width: 0 0 0 .25em;
+  font-style: italic;
+  padding-left: 0.5em;
+}</xsl:if>
+body {<xsl:if test="$xml2rfc-background!=''">
+  background: url(<xsl:value-of select="$xml2rfc-background" />) var(--col-bg) left top;</xsl:if>
+  background-color: var(--col-bg);
+  color: var(--col-fg);
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-body"/>;
+  font-size: 16px;
+  line-height: 1.5;
+  margin: 10px 0px 10px 10px;
+}<xsl:if test="$parsedMaxwidth!=''">
+@media screen and (min-width: <xsl:value-of select="number($parsedMaxwidth + 40)"/>px) {
+  body {
+    margin: 10px auto;
+    max-width: <xsl:value-of select="$parsedMaxwidth"/>px;
+  }
+}</xsl:if>
+samp, span.tt, code, pre {
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-pre"/>;
+}<xsl:if test="//xhtml:p">
+br.p {
+  line-height: 150%;
+}</xsl:if>
+cite {
+  font-style: normal;
+}<xsl:if test="//x:note|//aside">
+aside {
+  margin-left: 2em;
+}</xsl:if>
+dl {
+  margin-left: 2em;
+}
+dl > dt {
+  float: left;
+  margin-right: 1em;
+}
+dl.nohang > dt {
+  float: none;
+}
+dl > dd {
+  margin-bottom: .5em;
+}
+dl.compact > dd {
+  margin-bottom: .0em;
+}
+dl > dd > dl {
+  margin-top: 0.5em;
+}
+ul.empty {<!-- spacing between two entries in definition lists -->
+  list-style-type: none;
+}
+<xsl:if test="//ul[@bare='true']">ul.bare {
+  margin-left: -2em;
+}
+</xsl:if>ul.empty li {
+  margin-top: .5em;
+}
+dl p {
+  margin-left: 0em;
+}
+dl.<xsl:value-of select="$css-reference"/> > dt {
+  font-weight: bold;
+}
+dl.<xsl:value-of select="$css-reference"/> > dd {
+  margin-left: <xsl:choose><xsl:when test="$xml2rfc-symrefs='no'">3.5</xsl:when><xsl:otherwise>6</xsl:otherwise></xsl:choose>em;
+}
+h1 {
+  color: var(--col-fg-title);
+  font-size: 150%;
+  font-weight: bold;
+  text-align: center;
+  margin-top: 36pt;
+  margin-bottom: 0pt;
+}
+h2 {
+  font-size: 130%;
+  page-break-after: avoid;
+}
+h2.np {
+  page-break-before: always;
+}
+h3 {
+  font-size: 120%;
+  page-break-after: avoid;
+}
+h4 {
+  font-size: 110%;
+  page-break-after: avoid;
+}
+h5, h6 {
+  font-size: 100%;
+  page-break-after: avoid;
+}
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+  color: var(--col-fg);
+}
+img {
+  margin-left: 3em;
+}
+ol {
+  margin-left: 2em;
+}
+li ol {
+  margin-left: 0em;
+}
+ol p {
+  margin-left: 0em;
+}<xsl:if test="//xhtml:q">
+q {
+  font-style: italic;
+}</xsl:if>
+p {
+  margin-left: 2em;
+}
+pre {
+  font-size: 90%;
+  margin-left: 3em;
+  background-color: var(--col-bg-pre);
+  padding: .25em;
+  page-break-inside: avoid;
+}<xsl:if test="//artwork[@x:is-code-component='yes']|//sourcecode[@markers='true']"><!-- support "<CODE BEGINS>" and "<CODE ENDS>" markers-->
+pre.ccmarker {
+  background-color: var(--col-bg);
+  color: var(--col-fg-light);
+}
+pre.ccmarker > span {
+  font-size: small;
+}
+pre.cct {
+  margin-bottom: -1em;
+}
+pre.ccb {
+  margin-top: -1em;
+}</xsl:if>
+pre.text2 {
+  border-style: dotted;
+  border-width: 1px;
+  background-color: var(--col-bg-pre2);
+}
+pre.inline {
+  background-color: var(--col-bg);
+  padding: 0em;
+  page-break-inside: auto;<xsl:if test="$prettyprint-script!=''">
+  border: none !important;</xsl:if>
+}
+pre.text {
+  border-style: dotted;
+  border-width: 1px;
+  background-color: var(--col-bg-pre1);
+}
+pre.drawing {
+  border-style: solid;
+  border-width: 1px;
+  background-color: var(--col-bg-pre1);
+  padding: 2em;
+}<xsl:if test="//x:q">
+q {
+  font-style: italic;
+}</xsl:if>
+<xsl:if test="//x:sup|//sup">
+sup {
+  font-size: 60%;
+}</xsl:if><xsl:if test="//x:sub|//sub">
+sub {
+  font-size: 60%;
+}</xsl:if>
+table {
+  margin-left: 2em;
+}<xsl:if test="//texttable|//table">
+div.<xsl:value-of select="$css-tt"/> {
+  margin-left: 2em;
+} 
+table.<xsl:value-of select="$css-tt"/> {
+  border-collapse: collapse;
+  border-color: var(--col-fg-light);
+  border-spacing: 0; 
+  vertical-align: top;
+ }
+table.<xsl:value-of select="$css-tt"/> th {
+  border-color: var(--col-fg-light);
+  padding: 3px;
+}
+table.<xsl:value-of select="$css-tt"/> td {
+  border-color: var(--col-fg-light);
+  padding: 3px;
+}
+table.all {
+  border-style: solid;
+  border-width: 2px;
+}
+table.full {
+  border-style: solid;
+  border-width: 2px;
+}
+table.<xsl:value-of select="$css-tt"/> td {
+  vertical-align: top;
+}
+table.all td {
+  border-style: solid;
+  border-width: 1px;
+}
+table.full td {
+  border-style: none solid;
+  border-width: 1px;
+}
+table.<xsl:value-of select="$css-tt"/> th {
+  vertical-align: top;
+}
+table.all th {
+  border-style: solid;
+  border-width: 1px;
+}
+table.full th {
+  border-style: solid;
+  border-width: 1px 1px 2px 1px;
+}
+table.<xsl:value-of select="$css-tleft"/> {
+  margin-right: auto;
+}
+table.<xsl:value-of select="$css-tright"/> {
+  margin-left: auto;
+}
+table.<xsl:value-of select="$css-tcenter"/> {
+  margin-left: auto;
+  margin-right: auto;
+}
+caption {
+  caption-side: bottom;
+  font-weight: bold;
+  font-size: 80%;
+  margin-top: .5em;
+}
+<xsl:if test="//@x:caption-side">
+caption.caption-top {
+  caption-side: top;
+}
+</xsl:if>
+<xsl:if test="//table">
+table.v3 tr {
+    vertical-align: top;
+}
+table.v3 th {
+  background-color: var(--col-bg-th);
+  vertical-align: top;
+  padding: 0.25em 0.5em;
+}
+table.v3 td {
+  padding: 0.25em 0.5em;
+}
+table.v3 tr:nth-child(2n) > td {
+  background-color: var(--col-bg-tr);
+  vertical-align: top;
+}
+tr p {
+  margin-left: 0em;
+}
+tr pre {
+  margin-left: 1em;
+}
+tr ol {
+  margin-left: 1em;
+}
+tr ul {
+  margin-left: 1em;
+}
+tr dl {
+  margin-left: 1em;
+}
+</xsl:if>
+</xsl:if>
+table.<xsl:value-of select="$css-header"/> {
+  border-spacing: 1px;
+  width: 95%;
+  font-size: 90%;<xsl:if test="not(contains($styles,' header-bw '))">
+  color: var(--col-bg);</xsl:if>
+}
+td.top {
+  vertical-align: top;
+}
+td.topnowrap {
+  vertical-align: top;
+  white-space: nowrap;
+}
+table.<xsl:value-of select="$css-header"/> td {
+  vertical-align: top;<xsl:if test="not(contains($styles,' header-bw '))">
+  background-color: var(--col-bg-light);</xsl:if>
+  width: 50%;
+}<xsl:if test="/rfc/@obsoletes | /rfc/@updates">
+table.<xsl:value-of select="$css-header"/> a {
+  color: <xsl:choose><xsl:when test="not(contains($styles,' header-bw '))">var(--col-bg)</xsl:when><xsl:otherwise>var(--col-fg)</xsl:otherwise></xsl:choose>;
+}</xsl:if>
+ul.toc, ul.toc ul {
+  list-style: none;
+  margin-left: 1.5em;
+  padding-left: 0em;
+}
+ul.toc li {
+  line-height: 150%;
+  font-weight: bold;
+  margin-left: 0em;
+}
+ul.toc li li {
+  line-height: normal;
+  font-weight: normal;
+  font-size: 90%;
+  margin-left: 0em;
+}
+li.excluded {
+  font-size: 0%;
+}
+ul {
+  margin-left: 2em;
+}
+li ul {
+  margin-left: 0em;
+}
+ul p {
+  margin-left: 0em;
+}
+.filename, h1, h2, h3, h4 {
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-title"/>;
+}
+<xsl:if test="$has-index">ul.ind, ul.ind ul {
+  list-style: none;
+  margin-left: 1.5em;
+  padding-left: 0em;
+  page-break-before: avoid;
+}
+ul.ind li {
+  font-weight: bold;
+  line-height: 200%;
+  margin-left: 0em;
+}
+ul.ind li li {
+  font-weight: normal;
+  line-height: 150%;
+  margin-left: 0em;
+}</xsl:if><xsl:if test="//svg:svg">
+@namespace svg url(http://www.w3.org/2000/svg);
+svg|svg {
+  margin-left: 3em;
+}
+svg {
+  margin-left: 3em;
+}</xsl:if>
+.avoidbreakinside {
+  page-break-inside: avoid;
+}
+.avoidbreakafter {
+  page-break-after: avoid;
+}
+<xsl:if test="//t/@keepWithPrevious">.avoidbreakbefore {
+  page-break-before: avoid;
+}
+</xsl:if><xsl:if test="//*[@removeInRFC='true']">section.rfcEditorRemove > div:first-of-type {
+  font-style: italic;
+}</xsl:if><xsl:if test="//x:bcp14|//bcp14">.bcp14 {
+  font-style: normal;
+  text-transform: lowercase;
+  font-variant: small-caps;
+}</xsl:if><xsl:if test="//x:blockquote|//blockquote">
+blockquote > * .bcp14 {
+  font-style: italic;
+}</xsl:if>
+.comment {
+  background-color: var(--col-bg-highlight);
+}<xsl:if test="$xml2rfc-editing='yes'">
+.editingmark {
+  background-color: var(--col-bg-highlight);
+}</xsl:if>
+.<xsl:value-of select="$css-center"/> {
+  text-align: center;
+}
+.<xsl:value-of select="$css-error"/> {
+  color: var(--col-fg-error);
+  font-style: italic;
+  font-weight: bold;
+}
+.figure {
+  font-weight: bold;
+  text-align: center;
+  font-size: 80%;
+}
+.filename {
+  font-size: 112%;
+  font-weight: bold;
+  line-height: 21pt;
+  text-align: center;
+  margin-top: 0pt;
+}
+.fn {
+  font-weight: bold;
+}
+.<xsl:value-of select="$css-left"/> {
+  text-align: left;
+}
+.<xsl:value-of select="$css-right"/> {
+  text-align: right;
+}
+.warning {
+  font-size: 130%;
+  background-color: var(--col-bg-highlight);
+}<xsl:if test="$xml2rfc-ext-paragraph-links='yes'">
+.self {
+    color: var(--col-fg-light);
+    margin-left: .3em;
+    text-decoration: none;
+    visibility: hidden;
+    -webkit-user-select: none;<!-- not std CSS yet--> 
+    -moz-user-select: none;
+    -ms-user-select: none;
+}
+.self:hover {
+    text-decoration: none;
+}
+h1:hover > a.self, h2:hover > a.self, h3:hover > a.self, li:hover > a.self, p:hover > a.self {
+    visibility: visible;
+}</xsl:if><xsl:if test="$has-edits">del {
+  color: var(--col-fg-del);
+  text-decoration: line-through;
+}
+.del {
+  color: var(--col-fg-del);
+  text-decoration: line-through;
+}
+ins {
+  color: var(--col-fg-ins);
+  text-decoration: underline;
+}
+.ins {
+  color: var(--col-fg-ins);
+  text-decoration: underline;
+}
+div.issuepointer {
+  float: left;
+}</xsl:if><xsl:if test="//ed:issue">
+table.openissue {
+  background-color: var(--col-bg-highlight);
+  border-width: thin;
+  border-style: solid;
+  border-color: var(--col-fg);
+}
+table.closedissue {
+  background-color: var(--col-bg);
+  border-width: thin;
+  border-style: solid;
+  border-color: var(--col-fg-light);
+  color: var(--col-fg-light);
+}
+thead th {
+  text-align: left;
+}
+.bg-issue {
+  border: solid;
+  border-width: 1px;
+  font-size: 66%;
+}
+.closed-issue {
+  border: solid;
+  border-width: thin;
+  background-color: var(--col-bg-highlight2);
+  font-size: smaller;
+  font-weight: bold;
+}
+.open-issue {
+  border: solid;
+  border-width: thin;
+  background-color: var(--col-bg-error);
+  font-size: smaller;
+  font-weight: bold;
+}
+.editor-issue {
+  border: solid;
+  border-width: thin;
+  background-color: var(--col-bg-highlight);
+  font-size: smaller;
+  font-weight: bold;
+}</xsl:if><xsl:if test="$xml2rfc-ext-refresh-from!=''">.refreshxmlerror {
+  position: fixed;
+  top: 1%;
+  right: 1%;
+  padding: 5px 5px;
+  color: var(--col-bg-highlight);
+  background: var(--col-fg);
+}
+.refreshbrowsererror {
+  position: fixed;
+  top: 1%;
+  left: 1%;
+  padding: 5px 5px;
+  color: var(--col-bg-error);
+  background: var(--col-fg);
+}</xsl:if><xsl:if test="/rfc/x:feedback">.<xsl:value-of select="$css-feedback"/> {
+  position: fixed;
+  bottom: 1%;
+  right: 1%;
+  padding: 3px 5px;
+  color: var(--col-bg);
+  border-radius: 5px;
+  background: #006400;
+  border: 1px solid silver;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}
+.<xsl:value-of select="$css-fbbutton"/> {
+  margin-left: 1em;
+  color: #303030;
+  font-size: small;
+  font-weight: normal;
+  background: #d0d000;
+  padding: 1px 4px;
+  border: 1px solid silver;
+  border-radius: 5px;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}</xsl:if><xsl:if test="$xml2rfc-ext-justification='always'">
+dd, li, p {
+  text-align: justify;
+}</xsl:if><xsl:if test="$xml2rfc-ext-insert-metadata='yes' and ($is-rfc or $is-submitted-draft)">
+.<xsl:value-of select="$css-docstatus"/> {
+  border: 1px solid var(--col-fg);
+  display: none;
+  float: right;
+  margin: 2em;
+  padding: 1em;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}</xsl:if><xsl:if test="$errata-parsed">
+.<xsl:value-of select="$css-erratum"/> {
+  border: 1px solid orangered;
+  border-left: 0.75em solid orangered;
+  float: right;
+  padding: 0.5em;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}<xsl:if test="$parsedMaxwidth!=''">
+@media screen and (min-width: <xsl:value-of select="number($parsedMaxwidth + 350)"/>px) {
+  .<xsl:value-of select="$css-erratum"/> {
+    margin-right: -150px;
+  }
+}</xsl:if></xsl:if><xsl:if test="$published-as-rfc">
+.<xsl:value-of select="$css-publishedasrfc"/> {
+  background-color: var(--col-bg-highlight);
+  color: var(--col-fg);
+  font-size: 115%;
+  text-align: center;
+}</xsl:if><xsl:if test="$prettyprint-class='prettyprint' and contains($prettyprint-script,'prettify') and not(contains($prettyprint-script,'skin='))">
+  pre.prettyprint .pln { color: #000; }
+  pre.prettyprint .str, pre.prettyprint .atv { color: #080; }
+  pre.prettyprint .kwd, pre.prettyprint .tag { color: #008; }
+  pre.prettyprint .com { color: #800; }
+  pre.prettyprint .typ, pre.prettyprint .atn, pre.prettyprint .dec, pre.prettyprint .var { color: #606; }
+  pre.prettyprint .lit { color: #066; }
+  pre.prettyprint .pun, pre.prettyprint .opn, pre.prettyprint .clo { color: #660; }
+</xsl:if>
+
+@media screen {
+  pre.text, pre.text2, pre.drawing {
+    width: 69ch;
+  }
+}
+
+@media print {
+  .<xsl:value-of select="$css-noprint"/> {
+    display: none;
+  }
+
+  a {
+    color: black;
+    text-decoration: none;
+  }
+
+  table.<xsl:value-of select="$css-header"/> {
+    width: 90%;
+  }
+
+  td.<xsl:value-of select="$css-header"/> {
+    width: 50%;
+    color: black;
+    background-color: white;
+    vertical-align: top;
+    font-size: 110%;
+  }
+
+  ul.toc a:last-child::after {
+    content: leader('.') target-counter(attr(href), page);
+  }
+
+  ul.ind li li a {<!-- links in the leaf nodes of the index should go to page numbers -->
+    content: target-counter(attr(href), page);
+  }
+
+  .print2col {
+    column-count: 2;
+    -moz-column-count: 2;<!-- for Firefox -->
+    column-fill: auto;<!-- for PrinceXML -->
+  }
+<xsl:if test="$xml2rfc-ext-justification='print'">
+  dd, li, p {
+    text-align: justify;
+  }
+</xsl:if>}
+@page<xsl:if test="$xml2rfc-ext-duplex='yes'">:right</xsl:if> {
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-body"/>;
+  @top-left {
+       content: "<xsl:call-template name="get-header-left"/>";
+  }
+  @top-right {
+       content: "<xsl:call-template name="get-header-right"/>";
+  }
+  @top-center {
+       content: "<xsl:call-template name="get-header-center"/>";
+  }
+  @bottom-left {
+       content: "<xsl:call-template name="get-author-summary"/>";
+  }
+  @bottom-center {
+       content: "<xsl:call-template name="get-bottom-center"/>";
+  }
+  @bottom-right {
+       content: "[Page " counter(page) "]";
+  }
+}<xsl:if test="$xml2rfc-ext-duplex='yes'">
+@page:left {
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-body"/>;
+  @top-left {
+       content: "<xsl:call-template name="get-header-right"/>";
+  }
+  @top-right {
+       content: "<xsl:call-template name="get-header-left"/>";
+  }
+  @top-center {
+       content: "<xsl:call-template name="get-header-center"/>";
+  }
+  @bottom-left {
+       content: "[Page " counter(page) "]";
+  }
+  @bottom-center {
+       content: "<xsl:call-template name="get-bottom-center"/>";
+  }
+  @bottom-right {
+       content: "<xsl:call-template name="get-author-summary"/>";
+  }
+}
+</xsl:if>
+@page:first {
+    @top-left {
+      content: normal;
+    }
+    @top-right {
+      content: normal;
+    }
+    @top-center {
+      content: normal;
+    }
+}
+<xsl:if test="$xml2rfc-ext-dark-mode!='no'">
+@media (prefers-color-scheme: dark) {
+  :root {
+    --col-bg: black;
+    --col-bg-error: red;
+    --col-bg-highlight: #9e9e20;
+    --col-bg-highligh2: lime;
+    --col-bg-light: gray;
+    --col-bg-pre: #202000;
+    --col-bg-pre1: #080808;
+    --col-bg-pre2: #101010;
+    --col-bg-th: #303030;
+    --col-bg-tr: #202020;
+    --col-fg: white;
+    --col-fg-del: red;
+    --col-fg-error: red;
+    --col-fg-ins: green;
+    --col-fg-light: gray;
+    --col-fg-link: lightblue;
+    --col-fg-title: green;
+  }
+  
+  pre.prettyprint .pln { color: #fff; }
+  pre.prettyprint .str, pre.prettyprint .atv { color: #8f8; }
+  pre.prettyprint .kwd, pre.prettyprint .tag { color: #88f; }
+  pre.prettyprint .com { color: #f88; }
+  pre.prettyprint .typ, pre.prettyprint .atn, pre.prettyprint .dec, pre.prettyprint .var { color: #f8f; }
+  pre.prettyprint .lit { color: #8ff; }
+  pre.prettyprint .pun, pre.prettyprint .opn, pre.prettyprint .clo { color: #ff8; }
+}
+</xsl:if>
+</style>
+</xsl:template>
+
+
+<!-- generate the index section -->
+
+<xsl:template name="insertSingleIref">
+  <xsl:choose>
+    <xsl:when test="@ed:xref">
+      <!-- special index generator mode -->
+      <xsl:text>[</xsl:text>
+      <a href="#{@ed:xref}"><xsl:value-of select="@ed:xref"/></a>
+      <xsl:text>, </xsl:text>
+      <a>
+        <xsl:variable name="htmluri" select="//reference[@anchor=current()/@ed:xref]/format[@type='HTML']/@target"/>
+        <xsl:if test="$htmluri">
+          <xsl:attribute name="href"><xsl:value-of select="concat($htmluri,'#',@ed:frag)"/></xsl:attribute>
+        </xsl:if>
+        <xsl:choose>
+          <xsl:when test="@primary='true'"><b><xsl:value-of select="@ed:label" /></b></xsl:when>
+          <xsl:otherwise><xsl:value-of select="@ed:label" /></xsl:otherwise>
+        </xsl:choose>
+      </a>
+      <xsl:text>]</xsl:text>
+      <xsl:if test="position()!=last()">, </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="_n">
+        <xsl:call-template name="get-section-number" />
+      </xsl:variable>
+      <xsl:variable name="n">
+        <xsl:choose>
+          <xsl:when test="$_n!=''">
+            <xsl:value-of select="$_n"/>
+          </xsl:when>
+          <xsl:otherwise>&#167;</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <xsl:variable name="backlink">
+        <xsl:choose>
+          <xsl:when test="self::xref">
+            <xsl:variable name="target" select="@target"/>
+            <xsl:comment>workaround for Saxon 9.1 bug; force evaluation of: <xsl:value-of select="$target"/></xsl:comment>
+            <xsl:variable name="no"><xsl:number level="any" count="xref[@target=$target]"/></xsl:variable>
+            <xsl:text>#</xsl:text>
+            <xsl:value-of select="$anchor-pref"/>
+            <xsl:text>xref.</xsl:text>
+            <xsl:value-of select="@target"/>
+            <xsl:text>.</xsl:text>
+            <xsl:value-of select="$no"/>
+          </xsl:when>
+          <xsl:when test="self::iref">
+            <xsl:text>#</xsl:text>
+            <xsl:call-template name="compute-iref-anchor"/>
+          </xsl:when>
+          <xsl:when test="self::x:ref">
+            <xsl:text>#</xsl:text>
+            <xsl:call-template name="compute-extref-anchor"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:message>Unsupported element type for insertSingleIref</xsl:message>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <a href="{$backlink}">
+        <xsl:call-template name="insertInsDelClass"/>
+        <xsl:choose>
+          <xsl:when test="@primary='true'"><b><xsl:value-of select="$n"/></b></xsl:when>
+          <xsl:otherwise><xsl:value-of select="$n"/></xsl:otherwise>
+        </xsl:choose>
+      </a>
+      <xsl:if test="position()!=last()">, </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="insertSingleXref">
+  <xsl:variable name="_n">
+    <xsl:call-template name="get-section-number" />
+  </xsl:variable>
+  <xsl:variable name="n">
+    <xsl:choose>
+      <xsl:when test="$_n!=''">
+        <xsl:value-of select="$_n"/>
+      </xsl:when>
+      <xsl:otherwise>&#167;</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="self::reference">
+      <a href="#{@anchor}">
+        <xsl:call-template name="insertInsDelClass"/>
+        <b><xsl:value-of select="$n"/></b>
+      </a>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="target" select="@target"/>
+      <xsl:variable name="backlink">#<xsl:value-of select="$anchor-pref"/>xref.<xsl:value-of select="$target"/>.<xsl:number level="any" count="xref[@target=$target]|relref[@target=$target]"/></xsl:variable>
+      <a href="{$backlink}">
+        <xsl:call-template name="insertInsDelClass"/>
+        <xsl:value-of select="$n"/>
+      </a>
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:if test="position()!=last()">, </xsl:if>
+</xsl:template>
+
+<!-- generate navigation links to index subsections -->
+<xsl:template name="insert-index-navigation">
+  <p class="{$css-noprint}">
+
+    <xsl:for-each select="//iref | //reference[not(starts-with(@anchor,'deleted-'))]">
+    
+      <xsl:sort select="translate(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),$lcase,$ucase)" />
+
+      <xsl:variable name="letter" select="translate(substring(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),1,1),$lcase,$ucase)"/>
+      
+      <!-- first of character and character? -->
+      <xsl:if test="generate-id(.) = generate-id(key('index-first-letter',$letter)[1]) and translate($letter,$alnum,'')=''">
+        <xsl:variable name="showit" select="$xml2rfc-ext-include-references-in-index='yes' or self::iref"/>
+
+        <xsl:if test="$showit">
+          <a href="#{$anchor-pref}index.{$letter}">
+            <xsl:value-of select="$letter" />
+          </a>
+          <xsl:text> </xsl:text>
+        </xsl:if>
+      </xsl:if>
+    </xsl:for-each>
+  </p>
+</xsl:template>
+
+<xsl:template name="format-section-ref">
+  <xsl:param name="number"/>
+  <xsl:choose>
+    <xsl:when test="translate(substring($number,1,1),$ucase,'')=''">
+      <xsl:text>Appendix </xsl:text>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>Section </xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:value-of select="$number"/>
+</xsl:template>
+
+<xsl:template name="insert-index-item">
+  <xsl:param name="in-artwork"/>
+  <xsl:param name="irefs"/>
+  <xsl:param name="xrefs"/>
+  <xsl:param name="extrefs"/>
+
+  <xsl:choose>
+    <xsl:when test="$in-artwork">
+      <span class="tt"><xsl:value-of select="@item" /></span>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="@item" />
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:text>&#160;&#160;</xsl:text>
+
+  <xsl:for-each select="$irefs|$xrefs|$extrefs">
+    <xsl:call-template name="insertSingleIref" />
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="insert-index-subitem">
+  <xsl:param name="in-artwork"/>
+  <xsl:param name="irefs"/>
+  <xsl:param name="xrefs"/>
+  <xsl:param name="extrefs"/>
+
+  <li>
+    <xsl:choose>
+      <xsl:when test="$in-artwork">
+        <span class="tt"><xsl:value-of select="@subitem" /></span>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@subitem" />
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:text>&#160;&#160;</xsl:text>
+
+    <xsl:for-each select="$irefs|$xrefs|$extrefs">
+      <xsl:call-template name="insertSingleIref" />
+    </xsl:for-each>
+  </li>
+</xsl:template>
+
+
+<xsl:variable name="item-wrapper-element">li</xsl:variable>
+<xsl:attribute-set name="item-wrapper-element"/>
+<xsl:variable name="subitems-wrapper-element">ul</xsl:variable>
+
+<xsl:template name="insert-index-regular-iref">
+
+  <xsl:if test="generate-id(.) = generate-id(key('index-item',concat(@item,@anchor))[1])">
+    <xsl:variable name="item" select="@item"/>
+    <xsl:variable name="in-artwork" select="key('index-item',$item)[@primary='true' and ancestor::artwork]"/>
+
+    <xsl:element name="{$item-wrapper-element}" use-attribute-sets="item-wrapper-element">
+      <xsl:variable name="irefs3" select="key('index-item',@item)[not(@subitem) or @subitem='']"/>
+      <xsl:variable name="xrefs3" select="key('xref-item',$irefs3[@x:for-anchor='']/../@anchor) | key('xref-item',$irefs3/@x:for-anchor)"/>
+      <xsl:variable name="extrefs3" select="key('extref-item',$irefs3[@x:for-anchor='']/../@anchor) | key('extref-item',$irefs3/@x:for-anchor)"/>
+
+      <xsl:call-template name="insert-index-item">
+        <xsl:with-param name="in-artwork" select="key('index-item',@item)[@primary='true' and (ancestor::artwork or ancestor::sourcecode)]"/>
+        <xsl:with-param name="irefs" select="$irefs3"/>
+        <xsl:with-param name="xrefs" select="$xrefs3"/>
+        <xsl:with-param name="extrefs" select="$extrefs3"/>
+      </xsl:call-template>
+
+      <xsl:variable name="s2" select="key('index-item',@item)[@subitem!='']"/>
+      <xsl:if test="$s2">
+        <xsl:element name="{$subitems-wrapper-element}">
+          <xsl:for-each select="$s2">
+            <xsl:sort select="translate(@subitem,$lcase,$ucase)" />
+
+            <xsl:if test="generate-id(.) = generate-id(key('index-item-subitem',concat(@item,'..',@subitem))[1])">
+
+                <xsl:variable name="irefs4" select="key('index-item-subitem',concat(@item,'..',@subitem))"/>
+                <xsl:variable name="xrefs4" select="key('xref-item',$irefs4[@x:for-anchor='']/../@anchor) | key('xref-item',$irefs4/@x:for-anchor)"/>
+                <xsl:variable name="extrefs4" select="key('extref-item',$irefs4[@x:for-anchor='']/../@anchor) | key('extref-item',$irefs4/@x:for-anchor)"/>
+
+              <xsl:call-template name="insert-index-subitem">
+                <xsl:with-param name="in-artwork" select="key('index-item-subitem',concat(@item,'..',@subitem))[@primary='true' and (ancestor::artwork or ancestor::sourcecode)]"/>
+                <xsl:with-param name="irefs" select="$irefs4"/>
+                <xsl:with-param name="xrefs" select="$xrefs4"/>
+                <xsl:with-param name="extrefs" select="$extrefs4"/>
+              </xsl:call-template>
+            </xsl:if>
+          </xsl:for-each>
+        </xsl:element>
+      </xsl:if>
+    </xsl:element>
+  </xsl:if>
+</xsl:template>
+
+<!-- generate the index section -->
+
+<xsl:template name="insertIndex">
+
+  <xsl:call-template name="insert-conditional-hrule"/>
+
+  <section id="{$anchor-pref}index">
+    <xsl:call-template name="insert-conditional-pagebreak"/>
+    <h2>
+      <a href="#{$anchor-pref}index">Index</a>
+    </h2>
+
+    <xsl:call-template name="insert-index-navigation"/>
+    
+    <!-- for each index subsection -->
+    <div class="print2col">
+      <ul class="ind">
+        <xsl:for-each select="//iref | //reference[not(starts-with(@anchor,'deleted-'))]">
+          <xsl:sort select="translate(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),$lcase,$ucase)" />
+          <xsl:variable name="letter" select="translate(substring(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),1,1),$lcase,$ucase)"/>
+    
+          <xsl:variable name="showit" select="$xml2rfc-ext-include-references-in-index='yes' or self::iref"/>
+          <xsl:if test="$showit and generate-id(.) = generate-id(key('index-first-letter',$letter)[1])">
+            <li>
+    
+              <!-- make letters and digits stand out -->
+              <xsl:choose>
+                <xsl:when test="translate($letter,concat($lcase,$ucase,'0123456789'),'')=''">
+                  <a id="{$anchor-pref}index.{$letter}" href="#{$anchor-pref}index.{$letter}">
+                    <b><xsl:value-of select="$letter" /></b>
+                  </a>
+                </xsl:when>
+                <xsl:otherwise>
+                  <b><xsl:value-of select="$letter" /></b>
+                </xsl:otherwise>
+              </xsl:choose>
+    
+              <ul>
+                <xsl:for-each select="key('index-first-letter',$letter)">
+    
+                  <xsl:sort select="translate(concat(@item,@anchor),$lcase,$ucase)" />
+    
+                  <xsl:choose>
+                    <xsl:when test="self::reference">
+                      <xsl:if test="$xml2rfc-ext-include-references-in-index='yes' and not(starts-with(@anchor,'deleted-'))">
+                        <li>
+                          <xsl:variable name="val">
+                            <xsl:call-template name="reference-name"/>
+                          </xsl:variable>
+                          <em>
+                            <xsl:value-of select="substring($val,2,string-length($val)-2)"/>
+                          </em>
+                          <xsl:text>&#160;&#160;</xsl:text>
+    
+                          <xsl:variable name="rs" select="key('xref-item',current()/@anchor) | . | key('anchor-item',concat('deleted-',current()/@anchor))"/>
+    
+                          <xsl:for-each select="$rs">
+                            <xsl:call-template name="insertSingleXref" />
+                          </xsl:for-each>
+    
+                          <xsl:variable name="rs2" select="$rs[@x:sec|@section]"/>
+    
+                          <xsl:if test="$rs2">
+                            <ul>
+                              <xsl:for-each select="$rs2">
+                                <xsl:sort select="substring-before(concat(@x:sec,@section,'.'),'.')" data-type="number"/>
+                                <xsl:sort select="substring(concat(@x:sec,@section),2+string-length(substring-before(concat(@x:sec,@section),'.')))" data-type="number"/>
+    
+                                <xsl:if test="generate-id(.) = generate-id(key('index-xref-by-sec',concat(@target,'..',@x:sec,@section))[1])">
+                                  <li>
+                                    <em>
+                                      <xsl:call-template name="format-section-ref">
+                                        <xsl:with-param name="number" select="concat(@x:sec,@section)"/>
+                                      </xsl:call-template>
+                                    </em>
+                                    <xsl:text>&#160;&#160;</xsl:text>
+                                    <xsl:for-each select="key('index-xref-by-sec',concat(@target,'..',@x:sec,@section))">
+                                      <xsl:call-template name="insertSingleXref" />
+                                    </xsl:for-each>
+                                  </li>
+                                </xsl:if>
+                              </xsl:for-each>
+                            </ul>
+                          </xsl:if>
+    
+                          <xsl:if test="current()/x:source/@href">
+                            <xsl:variable name="rs3" select="$rs[not(@x:sec) and @x:rel]"/>
+                            <xsl:variable name="doc" select="document(current()/x:source/@href)"/>
+                            <xsl:if test="$rs3">
+                              <ul>
+                                <xsl:for-each select="$rs3">
+                                  <xsl:sort select="count($doc//*[@anchor and following::*/@anchor=substring-after(current()/@x:rel,'#')])" order="ascending" data-type="number"/>
+                                  <xsl:if test="generate-id(.) = generate-id(key('index-xref-by-anchor',concat(@target,'..',@x:rel))[1])">
+                                    <xsl:variable name="sec">
+                                      <xsl:for-each select="$doc//*[@anchor=substring-after(current()/@x:rel,'#')]">
+                                        <xsl:call-template name="get-section-number"/>
+                                      </xsl:for-each>
+                                    </xsl:variable>
+                                    <xsl:if test="$sec!=''">
+                                      <li>
+                                        <em>
+                                          <xsl:choose>
+                                            <xsl:when test="starts-with($sec,$unnumbered)">
+                                              <xsl:for-each select="$doc//*[@anchor=substring-after(current()/@x:rel,'#')]">
+                                                <xsl:call-template name="get-title-as-string"/>
+                                              </xsl:for-each>
+                                            </xsl:when>
+                                            <xsl:otherwise>
+                                              <xsl:call-template name="format-section-ref">
+                                                <xsl:with-param name="number" select="$sec"/>
+                                              </xsl:call-template>
+                                            </xsl:otherwise>
+                                          </xsl:choose>
+                                        </em>
+                                        <xsl:text>&#160;&#160;</xsl:text>
+                                        <xsl:for-each select="key('index-xref-by-anchor',concat(@target,'..',@x:rel))">
+                                          <xsl:call-template name="insertSingleXref" />
+                                        </xsl:for-each>
+                                      </li>
+                                    </xsl:if>
+                                  </xsl:if>
+                                </xsl:for-each>
+                              </ul>
+                            </xsl:if>
+                          </xsl:if>
+                        </li>
+                      </xsl:if>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      <xsl:call-template name="insert-index-regular-iref"/>
+                    </xsl:otherwise>
+                  </xsl:choose>
+                </xsl:for-each>
+              </ul>
+            </li>
+          </xsl:if>
+    
+        </xsl:for-each>
+      </ul>
+    </div>
+  </section>
+</xsl:template>
+
+<xsl:template name="insertPreamble" myns:namespaceless-elements="xml2rfc">
+
+  <xsl:param name="notes"/>
+
+<boilerplate>
+  <!-- TLP4, Section 6.c.iii -->
+  <xsl:variable name="pre5378EscapeClause">
+    This document may contain material from IETF Documents or IETF Contributions published or
+    made publicly available before November 10, 2008. The person(s) controlling the copyright in
+    some of this material may not have granted the IETF Trust the right to allow modifications of such
+    material outside the IETF Standards Process. Without obtaining an adequate license from the
+    person(s) controlling the copyright in such materials, this document may not be modified outside
+    the IETF Standards Process, and derivative works of it may not be created outside the IETF
+    Standards Process, except to format it for publication as an RFC or to translate it into languages
+    other than English.
+  </xsl:variable>
+
+  <!-- TLP1, Section 6.c.i -->
+  <xsl:variable name="noModificationTrust200811Clause">
+    This document may not be modified, and derivative works of it may not be
+    created, except to format it for publication as an RFC and to translate it
+    into languages other than English.
+  </xsl:variable>
+
+  <!-- TLP2..4, Section 6.c.i -->
+  <xsl:variable name="noModificationTrust200902Clause">
+    This document may not be modified, and derivative works of it may not be
+    created, except to format it for publication as an RFC or to translate it
+    into languages other than English.<!-- "and" changes to "or" -->
+  </xsl:variable>
+
+  <!-- TLP1..4, Section 6.c.ii -->
+  <xsl:variable name="noDerivativesTrust200___Clause">
+    This document may not be modified, and derivative works of it may not be
+    created, and it may not be published except as an Internet-Draft.
+  </xsl:variable>
+
+  <section anchor="{$anchor-pref}status">
+  <name>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-rfcedstyle='yes' or $src/rfc/@version >= 3">Status of This Memo</xsl:when>
+      <xsl:otherwise>Status of this Memo</xsl:otherwise>
+    </xsl:choose>
+  </name>
+
+  <xsl:choose>
+    <xsl:when test="@ipr and not($is-rfc)">
+      <t>
+        <xsl:choose>
+
+          <!-- RFC2026 -->
+          <xsl:when test="@ipr = 'full2026'">
+            This document is an Internet-Draft and is
+            in full conformance with all provisions of Section 10 of RFC2026.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativeWorks2026'">
+            This document is an Internet-Draft and is
+            in full conformance with all provisions of Section 10 of RFC2026
+            except that the right to produce derivative works is not granted.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativeWorksNow'">
+            This document is an Internet-Draft and is
+            in full conformance with all provisions of Section 10 of RFC2026
+            except that the right to produce derivative works is not granted.
+            (If this document becomes part of an IETF working group activity,
+            then it will be brought into full compliance with Section 10 of RFC2026.)
+          </xsl:when>
+          <xsl:when test="@ipr = 'none'">
+            This document is an Internet-Draft and is
+            NOT offered in accordance with Section 10 of RFC2026,
+            and the author does not provide the IETF with any rights other
+            than to publish as an Internet-Draft.
+          </xsl:when>
+
+          <!-- RFC3667 -->
+          <xsl:when test="@ipr = 'full3667'">
+            This document is an Internet-Draft and is subject to all provisions
+            of section 3 of RFC 3667.  By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she become aware will be disclosed, in accordance with
+            RFC 3668.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noModification3667'">
+            This document is an Internet-Draft and is subject to all provisions
+            of section 3 of RFC 3667.  By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she become aware will be disclosed, in accordance with
+            RFC 3668.  This document may not be modified, and derivative works of
+            it may not be created, except to publish it as an RFC and to
+            translate it into languages other than English<xsl:if test="@iprExtract">,
+            other than to extract <xref target="{@iprExtract}"/> as-is
+            for separate use</xsl:if>.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivatives3667'">
+            This document is an Internet-Draft and is subject to all provisions
+            of section 3 of RFC 3667 except for the right to produce derivative
+            works.  By submitting this Internet-Draft, each author represents
+            that any applicable patent or other IPR claims of which he or she
+            is aware have been or will be disclosed, and any of which he or she
+            become aware will be disclosed, in accordance with RFC 3668.  This
+            document may not be modified, and derivative works of it may
+            not be created<xsl:if test="@iprExtract">, other than to extract
+            <xref target="{@iprExtract}"/> as-is for separate use</xsl:if>.
+          </xsl:when>
+
+          <!-- RFC3978 -->
+          <xsl:when test="@ipr = 'full3978'">
+            By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she becomes aware will be disclosed, in accordance with
+            Section 6 of BCP 79.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noModification3978'">
+            By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she becomes aware will be disclosed, in accordance with
+            Section 6 of BCP 79.  This document may not be modified, and derivative works of
+            it may not be created, except to publish it as an RFC and to
+            translate it into languages other than English<xsl:if test="@iprExtract">,
+            other than to extract <xref target="{@iprExtract}"/> as-is
+            for separate use</xsl:if>.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivatives3978'">
+            By submitting this Internet-Draft, each author represents
+            that any applicable patent or other IPR claims of which he or she
+            is aware have been or will be disclosed, and any of which he or she
+            becomes aware will be disclosed, in accordance with Section 6 of BCP 79.  This
+            document may not be modified, and derivative works of it may
+            not be created<xsl:if test="@iprExtract">, other than to extract
+            <xref target="{@iprExtract}"/> as-is for separate use</xsl:if>.
+          </xsl:when>
+
+          <!-- as of Jan 2010, TLP 4.0 -->
+          <xsl:when test="$ipr-2010-01 and (@ipr = 'trust200902'
+                          or @ipr = 'noModificationTrust200902'
+                          or @ipr = 'noDerivativesTrust200902'
+                          or @ipr = 'pre5378Trust200902')">
+            This Internet-Draft is submitted in full conformance with
+            the provisions of BCP 78 and BCP 79.
+          </xsl:when>
+
+          <!-- as of Nov 2008, Feb 2009 and Sep 2009 -->
+          <xsl:when test="@ipr = 'trust200811'
+                          or @ipr = 'noModificationTrust200811'
+                          or @ipr = 'noDerivativesTrust200811'
+                          or @ipr = 'trust200902'
+                          or @ipr = 'noModificationTrust200902'
+                          or @ipr = 'noDerivativesTrust200902'
+                          or @ipr = 'pre5378Trust200902'">
+            This Internet-Draft is submitted to IETF in full conformance with
+            the provisions of BCP 78 and BCP 79.
+          </xsl:when>
+          <xsl:otherwise>
+            CONFORMANCE UNDEFINED.
+          </xsl:otherwise>
+        </xsl:choose>
+
+        <!-- warn about iprExtract without effect -->
+        <xsl:if test="@iprExtract and (@ipr != 'noModification3667' and @ipr != 'noDerivatives3667' and @ipr != 'noModification3978' and @ipr != 'noDerivatives3978')">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg" select="concat('/rfc/@iprExtract does not have any effect for /rfc/@ipr=',@ipr)"/>
+          </xsl:call-template>
+        </xsl:if>
+
+        <!-- restrictions -->
+        <xsl:choose>
+          <xsl:when test="@ipr = 'noModificationTrust200811'">
+            <xsl:value-of select="$noModificationTrust200811Clause"/>
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativesTrust200811'">
+            <xsl:value-of select="$noDerivativesTrust200___Clause"/>
+          </xsl:when>
+          <xsl:when test="@ipr = 'noModificationTrust200902'">
+            <xsl:value-of select="$noModificationTrust200902Clause"/>
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativesTrust200902'">
+            <xsl:value-of select="$noDerivativesTrust200___Clause"/>
+          </xsl:when>
+          <!-- escape clause moved to Copyright Notice as of 2009-11 -->
+          <xsl:when test="@ipr = 'pre5378Trust200902' and $pub-yearmonth &lt; 200911">
+            <xsl:value-of select="$pre5378EscapeClause"/>
+          </xsl:when>
+
+          <xsl:otherwise />
+        </xsl:choose>
+      </t>
+      <xsl:choose>
+        <xsl:when test="$id-boilerplate='2010'">
+          <xsl:variable name="current-uri">http<xsl:if test="$rfc-boilerplate-use-https">s</xsl:if>://datatracker.ietf.org/drafts/current/</xsl:variable>
+          <t>
+            Internet-Drafts are working documents of the Internet Engineering
+            Task Force (IETF). Note that other groups may also distribute
+            working documents as Internet-Drafts. The list of current
+            Internet-Drafts is at <eref target="{$current-uri}"><xsl:value-of select="$current-uri"/></eref>.
+          </t>
+        </xsl:when>
+        <xsl:otherwise>
+          <t>
+            Internet-Drafts are working documents of the Internet Engineering
+            Task Force (IETF), its areas, and its working groups.
+            Note that other groups may also distribute working documents as
+            Internet-Drafts.
+          </t>
+        </xsl:otherwise>
+      </xsl:choose>
+      <t>
+        Internet-Drafts are draft documents valid for a maximum of six months
+        and may be updated, replaced, or obsoleted by other documents at any time.
+        It is inappropriate to use Internet-Drafts as reference material or to cite
+        them other than as &#8220;work in progress&#8221;.
+      </t>
+      <xsl:if test="$id-boilerplate=''">
+        <t>
+          The list of current Internet-Drafts can be accessed at
+          <eref target='http://www.ietf.org/ietf/1id-abstracts.txt'>http://www.ietf.org/ietf/1id-abstracts.txt</eref>.
+        </t>
+        <t>
+          The list of Internet-Draft Shadow Directories can be accessed at
+          <eref target='http://www.ietf.org/shadow.html'>http://www.ietf.org/shadow.html</eref>.
+        </t>
+      </xsl:if>
+      <t>
+        This Internet-Draft will expire <xsl:call-template name="expirydate"><xsl:with-param name="in-prose" select="true()"/></xsl:call-template>.
+      </t>
+    </xsl:when>
+
+    <xsl:when test="@category='bcp' and $rfc-boilerplate='2010'">
+      <t>
+        This memo documents an Internet Best Current Practice.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='bcp'">
+      <t>
+        This document specifies an Internet Best Current Practices for the Internet
+        Community, and requests discussion and suggestions for improvements.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='exp' and $rfc-boilerplate='2010'">
+      <t>
+        This document is not an Internet Standards Track specification; it is
+        published for examination, experimental implementation, and evaluation.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='exp'">
+      <t>
+        This memo defines an Experimental Protocol for the Internet community.
+        It does not specify an Internet standard of any kind.
+        Discussion and suggestions for improvement are requested.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='historic' and $rfc-boilerplate='2010'">
+      <t>
+        This document is not an Internet Standards Track specification; it is
+        published for the historical record.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='historic'">
+      <t>
+        This memo describes a historic protocol for the Internet community.
+        It does not specify an Internet standard of any kind.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='std' and $rfc-boilerplate='2010'">
+      <t>
+        This is an Internet Standards Track document.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='std'">
+      <t>
+        This document specifies an Internet standards track protocol for the Internet
+        community, and requests discussion and suggestions for improvements.
+        Please refer to the current edition of the &#8220;Internet Official Protocol
+        Standards&#8221; (STD 1) for the standardization state and status of this
+        protocol. Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="(@category='info' or not(@category)) and $rfc-boilerplate='2010'">
+      <t>
+        This document is not an Internet Standards Track specification; it is
+        published for informational purposes.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='info' or not(@category)">
+      <t>
+        This memo provides information for the Internet community.
+        It does not specify an Internet standard of any kind.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:otherwise>
+      <t>
+        UNSUPPORTED CATEGORY.
+      </t>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported value for /rfc/@category: ', @category)"/>
+        <xsl:with-param name="inline" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <!-- 2nd and 3rd paragraph -->
+  <xsl:if test="$rfc-boilerplate='2010' and $is-rfc">
+    <t>
+      <xsl:if test="@category='exp'">
+        This document defines an Experimental Protocol for the Internet
+        community.
+      </xsl:if>
+      <xsl:if test="@category='historic'">
+        This document defines a Historic Document for the Internet community.
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="$submissionType='IETF'">
+          This document is a product of the Internet Engineering Task Force
+          (IETF).
+          <xsl:choose>
+            <xsl:when test="$consensus='yes'">
+              It represents the consensus of the IETF community.  It has
+              received public review and has been approved for publication by
+              the Internet Engineering Steering Group (IESG).
+            </xsl:when>
+            <xsl:otherwise>
+              It has been approved for publication by the Internet Engineering
+              Steering Group (IESG).
+              <!-- sanity check of $consensus -->
+              <xsl:if test="@category='std' or @category='bcp'">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg" select="'IETF BCPs and Standards Track documents require IETF consensus, check values of @category and @consensus!'"/>
+                  <xsl:with-param name="inline" select="'no'"/>
+                </xsl:call-template>
+              </xsl:if>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="$submissionType='IAB'">
+          This document is a product of the Internet Architecture Board (IAB)
+          and represents information that the IAB has deemed valuable to
+          provide for permanent record.
+          <xsl:if test="$consensus='yes'">
+            It represents the consensus of the Internet Architecture Board (IAB).
+          </xsl:if>
+        </xsl:when>
+        <xsl:when test="$submissionType='IRTF'">
+          This document is a product of the Internet Research Task Force (IRTF).
+          The IRTF publishes the results of Internet-related research and
+          development activities.  These results might not be suitable for
+          deployment.
+          <xsl:choose>
+            <xsl:when test="$consensus='yes' and front/workgroup[1]!=''">
+              This RFC represents the consensus of the
+              <xsl:value-of select="front/workgroup[1]"/> Research Group of the Internet
+              Research Task Force (IRTF).
+            </xsl:when>
+            <xsl:when test="$consensus='no' and front/workgroup[1]!=''">
+              This RFC represents the individual opinion(s) of one or more
+              members of the <xsl:value-of select="front/workgroup[1]"/> Research Group of the
+              Internet Research Task Force (IRTF).
+            </xsl:when>
+            <xsl:otherwise>
+              <!-- no research group -->
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="$submissionType='independent'">
+          This is a contribution to the RFC Series, independently of any other
+          RFC stream.  The RFC Editor has chosen to publish this document at
+          its discretion and makes no statement about its value for
+          implementation or deployment.
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- will contain error message already -->
+          <xsl:value-of select="$submissionType"/>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:variable name="candidates">
+        <!-- see https://www.rfc-editor.org/errata/eid5248 -->
+        <xsl:choose>
+          <xsl:when test="$pub-yearmonth &lt; 201802">a candidate</xsl:when>
+          <xsl:otherwise>candidates</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="$submissionType='IETF'">
+          <xsl:choose>
+            <xsl:when test="@category='bcp'">
+              Further information on BCPs is available in <xsl:copy-of select="$hab-reference"/>.
+            </xsl:when>
+            <xsl:when test="@category='std'">
+              Further information on Internet Standards is available in <xsl:copy-of select="$hab-reference"/>.
+            </xsl:when>
+            <xsl:otherwise>
+              Not all documents approved by the IESG are <xsl:value-of select="$candidates"/> for any
+              level of Internet Standard; see <xsl:copy-of select="$hab-reference"/>.
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:variable name="approver">
+            <xsl:choose>
+              <xsl:when test="$submissionType='IAB'">IAB</xsl:when>
+              <xsl:when test="$submissionType='IRTF'">IRSG</xsl:when>
+              <xsl:otherwise>RFC Editor</xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+
+          Documents approved for publication by the
+          <xsl:value-of select="$approver"/> are not <xsl:value-of select="$candidates"/> for any level
+          of Internet Standard; see <xsl:copy-of select="$hab-reference"/>.
+        </xsl:otherwise>
+      </xsl:choose>
+    </t>
+    <t>
+      Information about the current status of this document, any errata, and
+      how to provide feedback on it may be obtained at
+      <eref target="{$rfc-info-link}"><xsl:value-of select="$rfc-info-link"/></eref>.
+    </t>
+  </xsl:if>
+
+  </section>
+
+  <!-- some notes might go here; see http://www.rfc-editor.org/rfc-style-guide/rfc-style -->
+  <xsl:copy-of select="$notes"/>
+
+  <xsl:choose>
+    <xsl:when test="$src/rfc/@ipr='none'"/>
+    <xsl:when test="$ipr-2008-11">
+      <section anchor="{$anchor-pref}copyrightnotice">
+        <name>Copyright Notice</name>
+        <t>
+          Copyright (c) <xsl:value-of select="$xml2rfc-ext-pub-year" /> IETF Trust and the persons identified
+          as the document authors.  All rights reserved.
+        </t>
+        <xsl:choose>
+          <xsl:when test="$ipr-2010-01">
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents (<eref target="{$trust-license-info-link}"><xsl:value-of select="$trust-license-info-link"/></eref>)
+              in effect on the date of publication of this document. Please
+              review these documents carefully, as they describe your rights
+              and restrictions with respect to this document.
+              <xsl:if test="$submissionType='IETF'">
+                Code Components extracted from this document must include
+                Simplified BSD License text as described in Section 4.e of the
+                Trust Legal Provisions and are provided without warranty as
+                described in the Simplified BSD License.
+              </xsl:if>
+            </t>
+          </xsl:when>
+          <xsl:when test="$ipr-2009-09">
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>)
+              in effect on the date of publication of this document. Please
+              review these documents carefully, as they describe your rights
+              and restrictions with respect to this document. Code Components
+              extracted from this document must include Simplified BSD License
+              text as described in Section 4.e of the Trust Legal Provisions
+              and are provided without warranty as described in the BSD License.
+            </t>
+          </xsl:when>
+          <xsl:when test="$ipr-2009-02">
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents in effect on the date of
+              publication of this document
+              (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>).
+              Please review these documents carefully, as they describe your rights and restrictions with
+              respect to this document.
+            </t>
+          </xsl:when>
+          <xsl:otherwise>
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents
+              (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>) in effect on the date of
+              publication of this document.  Please review these documents
+              carefully, as they describe your rights and restrictions with respect
+              to this document.
+            </t>
+          </xsl:otherwise>
+        </xsl:choose>
+
+        <!-- add warning for incompatible IPR attribute on RFCs -->
+        <xsl:variable name="stds-rfc-compatible-ipr"
+                      select="@ipr='pre5378Trust200902' or @ipr='trust200902' or @ipr='trust200811' or @ipr='full3978' or @ipr='full3667' or @ipr='full2026'"/>
+
+        <xsl:variable name="rfc-compatible-ipr"
+                      select="$stds-rfc-compatible-ipr or @ipr='noModificationTrust200902' or @ipr='noDerivativesTrust200902' or @ipr='noModificationTrust200811' or @ipr='noDerivativesTrust200811'"/>
+                      <!-- TODO: may want to add more historic variants -->
+
+        <xsl:variable name="is-stds-track"
+                      select="$submissionType='IETF' and @category='std'"/>
+
+        <xsl:variable name="status-diags">
+          <xsl:choose>
+            <xsl:when test="$is-stds-track and $is-rfc and @ipr and not($stds-rfc-compatible-ipr)">
+              <xsl:value-of select="concat('The /rfc/@ipr attribute value of ',@ipr,' is not allowed on standards-track RFCs.')"/>
+            </xsl:when>
+            <xsl:when test="$is-rfc and @ipr and not($rfc-compatible-ipr)">
+              <xsl:value-of select="concat('The /rfc/@ipr attribute value of ',@ipr,' is not allowed on RFCs.')"/>
+            </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </xsl:variable>
+
+        <xsl:choose>
+          <xsl:when test="$status-diags!=''">
+            <t>
+              <spanx><xsl:value-of select="$status-diags"/></spanx>
+            </t>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="$status-diags"/>
+              <xsl:with-param name="inline" select="'no'"/>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:when test="($is-rfc or $pub-yearmonth >= 200911) and @ipr = 'pre5378Trust200902'">
+          <!-- special case: RFC5378 escape applies to RFCs as well -->
+          <!-- for IDs historically in Status Of This Memo, over here starting 2009-11 -->
+            <t>
+              <xsl:value-of select="$pre5378EscapeClause"/>
+            </t>
+          </xsl:when>
+          <xsl:when test="not($is-rfc)">
+            <!-- not an RFC, handled elsewhere -->
+          </xsl:when>
+          <xsl:when test="not(@ipr)">
+            <!-- no IPR value; done -->
+          </xsl:when>
+          <xsl:when test="@ipr='trust200902' or @ipr='trust200811' or @ipr='full3978' or @ipr='full3667' or @ipr='full2026'">
+            <!-- default IPR, allowed here -->
+          </xsl:when>
+          <xsl:when test="@ipr='noModificationTrust200811'">
+            <t>
+              <xsl:value-of select="$noModificationTrust200811Clause"/>
+            </t>
+          </xsl:when>
+          <xsl:when test="@ipr='noModificationTrust200902'">
+            <t>
+              <xsl:value-of select="$noModificationTrust200902Clause"/>
+            </t>
+          </xsl:when>
+          <xsl:when test="@ipr='noDerivativesTrust200902' or @ipr='noDerivativesTrust200811'">
+            <t>
+              <xsl:value-of select="$noDerivativesTrust200___Clause"/>
+            </t>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:variable name="msg" select="concat('unexpected value of /rfc/@ipr for this type of document: ',@ipr)"/>
+            <t>
+              <spanx><xsl:value-of select="$msg"/></spanx>
+            </t>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="$msg"/>
+              <xsl:with-param name="inline" select="'no'"/>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+
+      </section>
+    </xsl:when>
+    <xsl:when test="$ipr-2007-08">
+      <!-- no copyright notice -->
+    </xsl:when>
+    <xsl:when test="$ipr-rfc4748">
+      <section anchor="{$anchor-pref}copyrightnotice">
+        <name>Copyright Notice</name>
+        <t>
+          Copyright &#169; The IETF Trust (<xsl:value-of select="$xml2rfc-ext-pub-year" />).  All Rights Reserved.
+        </t>
+      </section>
+    </xsl:when>
+    <xsl:otherwise>
+      <section anchor="{$anchor-pref}copyrightnotice">
+        <name>Copyright Notice</name>
+        <t>
+          Copyright &#169; The Internet Society (<xsl:value-of select="$xml2rfc-ext-pub-year" />).  All Rights Reserved.
+        </t>
+      </section>
+    </xsl:otherwise>
+  </xsl:choose>
+</boilerplate>
+
+</xsl:template>
+
+<!-- TOC generation -->
+
+<xsl:template match="/" mode="toc">
+  <hr class="{$css-noprint}"/>
+
+  <nav id="{$anchor-pref}toc">
+    <xsl:call-template name="insert-errata">
+      <xsl:with-param name="section" select="'toc'"/>
+    </xsl:call-template>
+
+    <h2 class="np"> <!-- this pagebreak occurs always -->
+      <a href="#{$anchor-pref}toc">Table of Contents</a>
+    </h2>
+
+    <ul class="toc">
+      <xsl:apply-templates mode="toc" />
+    </ul>
+
+    <xsl:call-template name="insertTocAppendix" />
+  </nav>
+</xsl:template>
+
+<xsl:template name="insert-toc-line">
+  <xsl:param name="number" />
+  <xsl:param name="target" />
+  <xsl:param name="title" />
+  <xsl:param name="name" />
+  <xsl:param name="tocparam" />
+  <xsl:param name="oldtitle" />
+  <xsl:param name="waschanged" />
+
+  <xsl:variable name="depth">
+    <!-- count the dots -->
+    <xsl:choose>
+      <xsl:when test="starts-with($number,$unnumbered)">
+        <xsl:value-of select="string-length(translate(substring-after($number,$unnumbered),'.ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890&#167;','.'))"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="string-length(translate($number,'.ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890&#167;','.'))"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <!-- handle tocdepth parameter -->
+  <xsl:choose>
+    <xsl:when test="(not($tocparam) or $tocparam='' or $tocparam='default') and $depth >= $parsedTocDepth">
+      <!-- dropped entry because excluded -->
+      <xsl:attribute name="class">excluded</xsl:attribute>
+    </xsl:when>
+    <xsl:when test="$tocparam='exclude'">
+      <!-- dropped entry because excluded -->
+      <xsl:attribute name="class">excluded</xsl:attribute>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:choose>
+        <xsl:when test="starts-with($number,'del-')">
+          <del>
+            <xsl:value-of select="$number" />
+            <a href="#{$target}"><xsl:value-of select="$title"/></a>
+          </del>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:if test="$number != '' and not(contains($number,$unnumbered))">
+            <a href="#{$anchor-pref}section.{$number}">
+              <xsl:call-template name="emit-section-number">
+                <xsl:with-param name="no" select="$number"/>
+                <xsl:with-param name="appendixPrefix" select="true()"/>
+              </xsl:call-template>
+            </a>
+            <xsl:text>&#160;&#160;&#160;</xsl:text>
+          </xsl:if>
+          <a href="#{$target}">
+            <xsl:choose>
+              <xsl:when test="$waschanged!=''">
+                <ins><xsl:value-of select="$title"/></ins>
+                <del><xsl:value-of select="$oldtitle"/></del>
+              </xsl:when>
+              <xsl:when test="$name">
+                <xsl:call-template name="render-name-ref">
+                  <xsl:with-param name="n" select="$name/node()"/>
+                </xsl:call-template>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$title"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </a>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="back-toc">
+
+  <xsl:if test="//cref and $xml2rfc-comments='yes' and $xml2rfc-inline!='yes'">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'comments')"/>
+        <xsl:with-param name="title" select="'Editorial Comments'"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-ext-authors-section='before-appendices'">
+    <xsl:apply-templates select="/rfc/front" mode="toc" />
+  </xsl:if>
+  <xsl:apply-templates select="back/*[not(self::references)]" mode="toc" />
+
+  <!-- insert the index if index entries exist -->
+  <xsl:if test="$has-index">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'index')"/>
+        <xsl:with-param name="title" select="'Index'"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-ext-authors-section='end'">
+    <xsl:apply-templates select="/rfc/front" mode="toc" />
+  </xsl:if>
+
+  <!-- copyright statements -->
+  <xsl:if test="$xml2rfc-private='' and not($no-copylong)">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'ipr')"/>
+        <xsl:with-param name="title" select="'Intellectual Property and Copyright Statements'"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template match="front" mode="toc">
+
+  <xsl:variable name="authors-title">
+    <xsl:call-template name="get-authors-section-title"/>
+  </xsl:variable>
+  <xsl:variable name="authors-number">
+    <xsl:call-template name="get-authors-section-number"/>
+  </xsl:variable>
+
+  <xsl:if test="$authors-number!='suppress' and $xml2rfc-authorship!='no'">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'authors')"/>
+        <xsl:with-param name="title" select="$authors-title"/>
+        <xsl:with-param name="number" select="$authors-number"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="references-toc">
+
+  <!-- distinguish two cases: (a) single references element (process
+  as toplevel section; (b) multiple references sections (add one toplevel
+  container with subsection) -->
+
+  <xsl:variable name="refsecs" select="/rfc/back/references|/rfc/back/ed:replace/ed:ins/references"/>
+
+  <xsl:choose>
+    <xsl:when test="count($refsecs) = 0">
+      <!-- nop -->
+    </xsl:when>
+    <xsl:when test="count($refsecs) = 1">
+      <xsl:for-each select="$refsecs">
+        <xsl:variable name="title">
+          <xsl:choose>
+            <xsl:when test="@title!=''"><xsl:value-of select="@title" /></xsl:when>
+            <xsl:otherwise><xsl:value-of select="$xml2rfc-refparent"/></xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+
+        <li>
+          <xsl:call-template name="insert-toc-line">
+            <xsl:with-param name="number">
+              <xsl:call-template name="get-references-section-number"/>
+            </xsl:with-param>
+            <xsl:with-param name="target" select="concat($anchor-pref,'references')"/>
+            <xsl:with-param name="title" select="$title"/>
+            <xsl:with-param name="name" select="name"/>
+          </xsl:call-template>
+
+          <xsl:if test="references">
+            <ul>
+              <xsl:for-each select="references">
+                <xsl:call-template name="references-toc-entry"/>
+              </xsl:for-each>
+            </ul>
+          </xsl:if>
+        </li>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:otherwise>
+      <li>
+        <!-- insert pseudo container -->
+        <xsl:call-template name="insert-toc-line">
+          <xsl:with-param name="number">
+            <xsl:call-template name="get-references-section-number"/>
+          </xsl:with-param>
+          <xsl:with-param name="target" select="concat($anchor-pref,'references')"/>
+          <xsl:with-param name="title" select="$xml2rfc-refparent"/>
+        </xsl:call-template>
+
+        <ul>
+          <!-- ...with subsections... -->
+          <xsl:for-each select="$refsecs">
+            <xsl:call-template name="references-toc-entry"/>
+          </xsl:for-each>
+        </ul>
+      </li>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="references-toc-entry">
+  <xsl:variable name="title">
+    <xsl:choose>
+      <xsl:when test="@title!=''"><xsl:value-of select="@title" /></xsl:when>
+      <xsl:otherwise><xsl:value-of select="$xml2rfc-refparent"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="sectionNumber">
+    <xsl:call-template name="get-section-number" />
+  </xsl:variable>
+
+  <xsl:variable name="num">
+    <xsl:number level="any"/>
+  </xsl:variable>
+
+  <li>
+    <xsl:call-template name="insert-toc-line">
+      <xsl:with-param name="number" select="$sectionNumber"/>
+      <xsl:with-param name="target" select="concat($anchor-pref,'references','.',$num)"/>
+      <xsl:with-param name="title" select="$title"/>
+      <xsl:with-param name="name" select="name"/>
+    </xsl:call-template>
+
+    <xsl:if test="references">
+      <ul>
+        <xsl:for-each select="references">
+          <xsl:call-template name="references-toc-entry"/>
+        </xsl:for-each>
+      </ul>
+    </xsl:if>
+  </li>
+</xsl:template>
+
+<!-- suppress xml2rfc preptool artefacts -->
+<xsl:template match="section[author]" mode="toc"/>
+
+<xsl:template match="section|appendix" mode="toc">
+  <xsl:variable name="sectionNumber">
+    <xsl:call-template name="get-section-number" />
+  </xsl:variable>
+
+  <xsl:variable name="target">
+    <xsl:choose>
+      <xsl:when test="@anchor"><xsl:value-of select="@anchor" /></xsl:when>
+       <xsl:otherwise><xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$sectionNumber" /></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <!-- obtain content, just to check whether we need to recurse at all -->
+  <xsl:variable name="content">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="number" select="$sectionNumber"/>
+        <xsl:with-param name="target" select="$target"/>
+        <xsl:with-param name="title" select="@title"/>
+        <xsl:with-param name="name" select="name"/>
+        <xsl:with-param name="tocparam" select="@toc"/>
+        <xsl:with-param name="oldtitle" select="@ed:old-title"/>
+        <xsl:with-param name="waschanged" select="@ed:resolves"/>
+      </xsl:call-template>
+
+      <ul>
+        <xsl:apply-templates mode="toc" />
+      </ul>
+    </li>
+  </xsl:variable>
+
+  <xsl:if test="$content!=''">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="number" select="$sectionNumber"/>
+        <xsl:with-param name="target" select="$target"/>
+        <xsl:with-param name="title" select="@title"/>
+        <xsl:with-param name="name" select="name"/>
+        <xsl:with-param name="tocparam" select="@toc"/>
+        <xsl:with-param name="oldtitle" select="@ed:old-title"/>
+        <xsl:with-param name="waschanged" select="@ed:resolves"/>
+      </xsl:call-template>
+
+      <!-- obtain nested content, just to check whether we need to recurse at all -->
+      <xsl:variable name="nested-content">
+        <ul>
+          <xsl:apply-templates mode="toc" />
+        </ul>
+      </xsl:variable>
+
+      <!-- only recurse if we need to (do not produce useless list container) -->
+      <xsl:if test="$nested-content!=''">
+        <ul>
+          <xsl:apply-templates mode="toc" />
+        </ul>
+      </xsl:if>
+    </li>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="middle" mode="toc">
+  <xsl:apply-templates mode="toc" />
+  <xsl:call-template name="references-toc" />
+</xsl:template>
+
+<xsl:template match="rfc" mode="toc">
+  <xsl:apply-templates select="middle" mode="toc" />
+  <xsl:call-template name="back-toc" />
+</xsl:template>
+
+<xsl:template match="ed:del|ed:ins|ed:replace" mode="toc">
+  <xsl:apply-templates mode="toc" />
+</xsl:template>
+
+<xsl:template match="*|text()" mode="toc" />
+
+
+<xsl:template name="insertTocAppendix">
+
+  <xsl:if test="//figure[@title!='' or @anchor!='' or name]">
+    <ul class="toc">
+      <li>
+        <xsl:text>Figures</xsl:text>
+        <ul>
+          <xsl:for-each select="//figure[@title!='' or @anchor!='' or name]">
+            <xsl:variable name="n"><xsl:call-template name="get-figure-number"/></xsl:variable>
+            <xsl:variable name="title">
+              <xsl:if test="not(starts-with($n,'u'))">
+                <xsl:text>Figure </xsl:text>
+                <xsl:value-of select="$n"/>
+                <xsl:if test="@title!='' or name">: </xsl:if>
+              </xsl:if>
+              <xsl:choose>
+                <xsl:when test="name">
+                  <xsl:call-template name="render-name-ref">
+                    <xsl:with-param name="n" select="name/node()"/>
+                  </xsl:call-template>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="normalize-space(@title)" />
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:variable>
+            <li>
+              <xsl:call-template name="insert-toc-line">
+                <xsl:with-param name="target" select="concat($anchor-pref,'figure.',$n)" />
+                <xsl:with-param name="title" select="$title" />
+              </xsl:call-template>
+            </li>
+          </xsl:for-each>
+        </ul>
+      </li>
+    </ul>
+  </xsl:if>
+
+  <!-- experimental -->
+  <xsl:if test="//ed:issue">
+    <xsl:call-template name="insertIssuesList" />
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="reference-name">
+  <xsl:param name="node" select="."/>
+
+  <xsl:for-each select="$node">
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-symrefs!='no' and ancestor::ed:del">
+        <xsl:variable name="unprefixed" select="substring-after(@anchor,'deleted-')"/>
+        <xsl:choose>
+          <xsl:when test="$unprefixed!=''">
+            <xsl:value-of select="concat('[',$unprefixed,']')"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:if test="count(//reference[@anchor=current()/@anchor])!=1">
+              <xsl:message>Deleted duplicate anchors should have the prefix "deleted-": <xsl:value-of select="@anchor"/></xsl:message>
+            </xsl:if>
+            <xsl:value-of select="concat('[',@anchor,']')"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="$xml2rfc-symrefs!='no'">
+        <xsl:text>[</xsl:text>
+        <xsl:choose>
+          <xsl:when test="$src/rfc/back/displayreference[@target=current()/@anchor]">
+            <xsl:value-of select="$src/rfc/back/displayreference[@target=current()/@anchor]/@to"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="@anchor"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:text>]</xsl:text>
+      </xsl:when>
+      <xsl:when test="ancestor::ed:del">
+        <xsl:text>[del]</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>[<xsl:number level="any" count="reference[not(ancestor::ed:del)]"/>]</xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+
+
+<xsl:template name="replace-substring">
+  <xsl:param name="string" />
+  <xsl:param name="replace" />
+  <xsl:param name="by" />
+
+  <xsl:choose>
+    <xsl:when test="contains($string,$replace)">
+      <xsl:value-of select="concat(substring-before($string, $replace),$by)" />
+      <xsl:call-template name="replace-substring">
+        <xsl:with-param name="string" select="substring-after($string,$replace)" />
+        <xsl:with-param name="replace" select="$replace" />
+        <xsl:with-param name="by" select="$by" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="rfc-or-id-link">
+  <xsl:param name="name" />
+
+  <xsl:choose>
+    <xsl:when test="starts-with($name,'draft-')">
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-internet-draft-uri">
+          <xsl:with-param name="internet-draft" select="$name"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <a href="{$uri}"><xsl:value-of select="$name"/></a>
+      <xsl:call-template name="check-front-matter-ref">
+        <xsl:with-param name="name" select="$name"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="string(number($name))=$name">
+      <xsl:variable name="uri">
+        <xsl:variable name="refs" select="exslt:node-set($includeDirectives)//reference|/rfc/back/references//reference"/>
+        <xsl:variable name="ref" select="$refs[not(starts-with(front/title,'Erratum ID')) and seriesInfo[@name='RFC' and @value=$name]]"/>
+        <xsl:choose>
+          <xsl:when test="$ref">
+            <xsl:value-of select="concat('#',$ref/@anchor)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="compute-rfc-uri">
+              <xsl:with-param name="rfc" select="$name"/>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <a href="{$uri}"><xsl:value-of select="$name"/></a>
+      <xsl:call-template name="check-front-matter-ref">
+        <xsl:with-param name="name" select="$name"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$name"/>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('In metadata obsoletes/updates, RFC number of draft name is expected - found: ',$name)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="rfclist">
+  <xsl:param name="list" />
+  <xsl:choose>
+    <xsl:when test="contains($list,',')">
+      <xsl:variable name="rfcNo" select="substring-before($list,',')" />
+      <xsl:call-template name="rfc-or-id-link">
+        <xsl:with-param name="name" select="$rfcNo"/>
+      </xsl:call-template>
+      <xsl:text>, </xsl:text>
+      <xsl:call-template name="rfclist">
+        <xsl:with-param name="list" select="normalize-space(substring-after($list,','))" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="rfcNo" select="$list" />
+      <xsl:call-template name="rfc-or-id-link">
+        <xsl:with-param name="name" select="$rfcNo"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="check-front-matter-ref">
+  <xsl:param name="name"/>
+  <xsl:variable name="refs" select="exslt:node-set($includeDirectives)//reference|/rfc/back/references//reference"/>
+  <xsl:choose>
+    <xsl:when test="starts-with($name,'draft-')">
+      <xsl:if test="not($refs//seriesInfo[@name='Internet-Draft' and @value=$name])">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('front matter mentions I-D ',$name,' for which there is no reference element')"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="ref" select="$refs[not(starts-with(front/title,'Erratum ID')) and seriesInfo[@name='RFC' and @value=$name]]"/>
+      <xsl:if test="not($ref)">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('front matter mentions RFC ',$name,' for which there is no reference element')"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="check-anchor">
+  <xsl:if test="@anchor and @anchor!=''">
+    <!-- check validity of anchor name -->
+    <xsl:variable name="t" select="@anchor"/>
+    <xsl:variable name="tstart" select="substring($t,1,1)"/>
+
+    <!-- we only check for disallowed ASCII characters for now -->
+    <xsl:variable name="not-namestartchars">&#9;&#10;&#13;&#32;!"#$%&amp;'()*+,-./0123456789;&lt;=&gt;?@[\]^`[|}~</xsl:variable>
+
+    <xsl:if test="$tstart!=translate($tstart,$not-namestartchars,'')">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('anchor &quot;',$t,'&quot; can not start with character &quot;',$tstart,'&quot;')"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:call-template name="check-anchor-non-start">
+      <xsl:with-param name="f" select="$t"/>
+      <xsl:with-param name="t" select="$t"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="check-anchor-non-start">
+  <xsl:param name="f"/>
+  <xsl:param name="t"/>
+
+  <xsl:variable name="not-namechars">&#9;&#10;&#13;&#32;!"#$%&amp;'()*+,/;&lt;=&gt;?@[\]^`[|}~</xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$t=''">
+      <!-- Done -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="s" select="substring($t,1,1)"/>
+      <xsl:choose>
+        <xsl:when test="$s!=translate($s,$not-namechars,'')">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('anchor &quot;',$f,'&quot; contains invalid character &quot;',$s,'&quot; at position ',string-length($f) - string-length($t))"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="check-anchor-non-start">
+            <xsl:with-param name="f" select="$f"/>
+            <xsl:with-param name="t" select="substring($t,2)"/>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="sluggy-anchor">
+  <xsl:if test="self::section and (not(@anchor) or @anchor='')">
+    <xsl:variable name="fr">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.'"()+-_ :!%,/@=&lt;&gt;*&#8212;&#8232;</xsl:variable>
+    <xsl:variable name="to">abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789.__----_--.-------.--</xsl:variable>
+    <xsl:variable name="canslug" select="translate(normalize-space(concat(@title,name)),$fr,'')=''"/>
+    <xsl:if test="$canslug">
+      <xsl:variable name="slug" select="translate(normalize-space(concat(@title,name)),$fr,$to)"/>
+      <xsl:variable name="conflicts" select="//section[not(@anchor) and $slug=translate(normalize-space(concat(@title,name)),$fr,$to)]"/>
+      <xsl:choose>
+        <xsl:when test="count($conflicts)>1">
+          <xsl:variable name="c" select="preceding::*[not(@anchor) and $slug=translate(normalize-space(concat(@title,name)),$fr,$to)]"/>
+          <xsl:value-of select="concat('n-',$slug,'_',(1+count($c)))"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="concat('n-',$slug)"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="copy-anchor">
+  <xsl:call-template name="check-anchor"/>
+  <xsl:choose>
+    <xsl:when test="@anchor and @anchor!=''">
+      <xsl:attribute name="id"><xsl:value-of select="@anchor"/></xsl:attribute>
+    </xsl:when>
+    <xsl:when test="self::section">
+      <xsl:variable name="slug">
+        <xsl:call-template name="sluggy-anchor"/>
+      </xsl:variable>
+      <xsl:if test="$slug!=''">
+        <xsl:attribute name="id"><xsl:value-of select="$slug"/></xsl:attribute>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="rfclist-for-dcmeta">
+  <xsl:param name="list" />
+  <xsl:choose>
+    <xsl:when test="contains($list,',')">
+      <xsl:variable name="rfcNo" select="substring-before($list,',')" />
+      <meta name="dct.replaces" content="urn:ietf:rfc:{$rfcNo}" />
+      <xsl:call-template name="rfclist-for-dcmeta">
+        <xsl:with-param name="list" select="normalize-space(substring-after($list,','))" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="rfcNo" select="$list" />
+      <meta name="dct.replaces" content="urn:ietf:rfc:{$rfcNo}" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-paragraph-number">
+  <xsl:choose>
+    <!-- inside artset -->
+    <xsl:when test="parent::artset">
+      <xsl:for-each select="..">
+        <xsl:call-template name="get-paragraph-number"/>
+      </xsl:for-each>
+    </xsl:when>
+
+    <!-- no numbering inside certain containers -->
+    <xsl:when test="ancestor::dl or ancestor::figure or ancestor::ol or ancestor::ul or ancestor::ed:del or ancestor::ed:ins"/>
+  
+    <xsl:when test="parent::blockquote or parent::x:blockquote">
+      <!-- boilerplate -->
+      <xsl:for-each select="parent::blockquote|parent::x:blockquote"><xsl:call-template name="get-paragraph-number" />.</xsl:for-each>
+      <xsl:number count="artset|artwork|aside|blockquote|dl|ol|sourcecode|t|ul|x:blockquote|x:note"/>
+    </xsl:when>
+
+    <xsl:when test="parent::aside or parent::x:note">
+      <!-- boilerplate -->
+      <xsl:for-each select="parent::aside|parent::x:note"><xsl:call-template name="get-paragraph-number" />.</xsl:for-each>
+      <xsl:number count="artset|artwork|aside|blockquote|dl|ol|sourcecode|t|ul|x:blockquote|x:note"/>
+    </xsl:when>
+
+    <xsl:when test="ancestor::section">
+      <!-- get section number of ancestor section element, then add t number -->
+      <xsl:for-each select="ancestor::section[1]"><xsl:call-template name="get-section-number" />.p.</xsl:for-each>
+      <xsl:variable name="b"><xsl:number count="artset|artwork|aside|blockquote|dl|ol|sourcecode|t|ul|x:blockquote|x:note"/></xsl:variable>
+      <xsl:choose>
+        <xsl:when test="parent::section and ../@removeInRFC='true' and ../t[1]!=$section-removeInRFC">
+          <xsl:value-of select="1 + $b"/>
+        </xsl:when>
+        <xsl:otherwise><xsl:value-of select="$b"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+
+    <xsl:when test="ancestor::note">
+      <!-- get section number of ancestor note element, then add t number -->
+      <xsl:for-each select="ancestor::note[1]"><xsl:call-template name="get-section-number" />.p.</xsl:for-each>
+      <xsl:variable name="b"><xsl:number count="artset|artwork|aside|blockquote|dl|ol|sourcecode|t|ul|x:blockquote|x:note"/></xsl:variable>
+      <xsl:choose>
+        <xsl:when test="parent::note and ../@removeInRFC='true' and ../t[1]!=$note-removeInRFC">
+          <xsl:value-of select="1 + $b"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$b"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+
+    <!-- abstract -->
+    <xsl:when test="ancestor::abstract">
+      <xsl:text>p.</xsl:text>
+      <xsl:number count="t|x:blockquote|blockquote|x:note|aside|ul|dl|ol|artwork|artset|sourcecode"/>
+    </xsl:when>
+
+    <xsl:otherwise/>
+  </xsl:choose>  
+</xsl:template>
+
+<xsl:template name="attach-paragraph-number-as-id">
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number"/>
+  </xsl:variable>
+  <xsl:variable name="container">
+    <xsl:choose>
+      <xsl:when test="ancestor::abstract">abstract</xsl:when>
+      <xsl:when test="ancestor::note">note</xsl:when>
+      <xsl:when test="ancestor::boilerplate">boilerplate</xsl:when>
+      <xsl:otherwise>section</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:if test="$p!='' and not(ancestor::list)">
+    <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,$container,'.',$p)"/></xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="editingMark">
+  <xsl:if test="$xml2rfc-editing='yes' and ancestor::rfc">
+    <sup class="editingmark"><span><xsl:number level="any" count="postamble|preamble|t"/></span>&#0160;</sup>
+  </xsl:if>
+</xsl:template>
+
+<!-- internal ref support -->
+<xsl:key name="anchor-item-alias" match="//*[@anchor and (x:anchor-alias/@value or ed:replace/ed:ins/x:anchor-alias)]" use="x:anchor-alias/@value | ed:replace/ed:ins/x:anchor-alias/@value"/>
+
+<xsl:template match="x:ref">
+  <xsl:variable name="val" select="normalize-space(.)"/>
+  <xsl:variable name="target" select="key('anchor-item',$val) | key('anchor-item-alias',$val) | //reference/x:source[x:defines=$val]"/>
+  <xsl:if test="count($target)>1">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">internal link target for '<xsl:value-of select="."/>' is ambiguous; picking first.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:choose>
+    <xsl:when test="$target[1]/@anchor">
+      <a href="#{$target[1]/@anchor}" class="smpl">
+        <xsl:call-template name="copy-anchor"/>
+        <!-- insert id when a backlink to this xref is needed in the index -->
+        <xsl:if test="//iref[@x:for-anchor=$val] | //iref[@x:for-anchor='' and ../@anchor=$val]">
+          <xsl:attribute name="id"><xsl:call-template name="compute-extref-anchor"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="."/>
+      </a>
+    </xsl:when>
+    <xsl:when test="$target[1]/self::x:source">
+      <xsl:variable name="extdoc" select="document($target[1]/@href)"/>
+      <xsl:variable name="nodes" select="$extdoc//*[@anchor and (x:anchor-alias/@value=$val)]"/>
+      <xsl:choose>
+        <xsl:when test="not($nodes)">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' not found in source file '<xsl:value-of select="$target[1]/@href"/>'.</xsl:with-param>
+          </xsl:call-template>
+          <xsl:value-of select="."/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:variable name="t">
+            <xsl:call-template name="computed-auto-target">
+              <xsl:with-param name="bib" select="$target[1]/.."/>
+              <xsl:with-param name="ref" select="$nodes[1]"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <a href="{$t}" class="smpl">
+            <xsl:value-of select="."/>
+          </a>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="//x:source">
+      <xsl:variable name="ref" select="."/>
+      <xsl:variable name="out">
+        <!-- try referenced documents one by one -->
+        <xsl:for-each select="//reference[x:source]">
+          <xsl:variable name="extdoc" select="document(x:source/@href)"/>
+          <xsl:variable name="nodes" select="$extdoc//*[@anchor and (x:anchor-alias/@value=$val)]"/>
+          <xsl:choose>
+            <xsl:when test="not($nodes)">
+              <xsl:call-template name="trace">
+                <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' not found in source file '<xsl:value-of select="x:source/@href"/>'.</xsl:with-param>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:call-template name="info">
+                <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' found in source file '<xsl:value-of select="x:source/@href"/>'.</xsl:with-param>
+              </xsl:call-template>
+              <xsl:variable name="t">
+                <xsl:call-template name="computed-auto-target">
+                  <xsl:with-param name="ref" select="$nodes[1]"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <a href="{$t}" class="smpl">
+                <xsl:value-of select="$ref"/>
+              </a>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:copy-of select="$out"/>
+      <xsl:variable name="plainout" select="normalize-space($out)"/>
+      <xsl:if test="string-length($plainout)=0">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' not found anywhere in references.</xsl:with-param>
+        </xsl:call-template>
+        <xsl:value-of select="$val"/>
+      </xsl:if>
+      <xsl:if test="string-length($plainout)!=string-length($val)">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Multiple targets found for anchor '<xsl:value-of select="$val"/>' - need to disambiguate.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">internal link target for '<xsl:value-of select="."/>' does not exist.</xsl:with-param>
+      </xsl:call-template>
+      <xsl:value-of select="."/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- Nothing to do here -->
+<xsl:template match="x:anchor-alias" />
+
+<!-- Quotes -->
+<xsl:template match="x:q">
+  <q>
+    <xsl:copy-of select="@cite"/>
+    <xsl:apply-templates/>
+  </q>
+</xsl:template>
+
+<!-- Notes -->
+<xsl:template match="x:note|aside">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <div>
+    <xsl:call-template name="attach-paragraph-number-as-id"/>
+    <aside>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:apply-templates select="*"/>
+    </aside>
+  </div>
+</xsl:template>
+
+<xsl:template match="x:bcp14|bcp14">
+  <!-- check valid BCP14 keywords, then emphasize them -->
+  <xsl:variable name="c" select="normalize-space(translate(.,'&#160;',' '))"/>
+  <xsl:choose>
+    <xsl:when test="$c='MUST' or $c='REQUIRED' or $c='SHALL' or $c='MUST NOT'
+      or $c='SHALL NOT' or $c='SHOULD' or $c='RECOMMENDED' or $c='SHOULD NOT'
+      or $c='NOT RECOMMENDED' or $c='MAY' or $c='OPTIONAL'">
+      <em class="bcp14"><xsl:value-of select="."/></em>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="."/>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unknown BCP14 keyword: ',$c)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:blockquote|blockquote">
+  <div>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:call-template name="editingMark" />
+    <xsl:call-template name="attach-paragraph-number-as-id"/>
+    <blockquote>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:copy-of select="@cite"/>
+      <xsl:choose>
+        <xsl:when test="t|ul|ol|dl|artwork|figure|sourcecode">
+          <xsl:apply-templates/>
+        </xsl:when>
+        <xsl:otherwise>
+          <p>
+            <xsl:apply-templates/>
+          </p>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:if test="@quotedFrom">
+        <cite>
+          <xsl:text>&#8212; </xsl:text>
+          <xsl:choose>
+            <xsl:when test="@cite"><a href="{@cite}"><xsl:value-of select="@quotedFrom"/></a></xsl:when>
+            <xsl:otherwise><xsl:value-of select="@quotedFrom"/></xsl:otherwise>
+          </xsl:choose>
+        </cite>
+      </xsl:if>
+    </blockquote>
+  </div>
+</xsl:template>
+
+<!-- Definitions -->
+<xsl:template match="x:dfn">
+  <dfn>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </dfn>
+</xsl:template>
+
+<!-- headings -->
+<xsl:template match="x:h">
+  <b>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </b>
+</xsl:template>
+
+<!-- superscripts -->
+<xsl:template match="x:sup|sup">
+  <sup>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </sup>
+</xsl:template>
+
+<!-- subscripts -->
+<xsl:template match="sub">
+  <sub>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </sub>
+</xsl:template>
+
+<!-- bold -->
+<xsl:template match="x:highlight">
+  <b>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </b>
+</xsl:template>
+
+<!-- measuring lengths -->
+<xsl:template match="x:length-of">
+  <xsl:variable name="target" select="//*[@anchor=current()/@target]"/>
+  <xsl:if test="count($target)!=1">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg" select="concat('@target ',@target,' defined ',count($target),' times.')"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:variable name="content">
+    <xsl:apply-templates select="$target"/>
+  </xsl:variable>
+  <xsl:variable name="lineends" select="string-length($content) - string-length(translate($content,'&#10;',''))"/>
+  <xsl:variable name="indents">
+    <xsl:choose>
+      <xsl:when test="@indented">
+        <xsl:value-of select="number(@indented) * $lineends"/>
+      </xsl:when>
+      <xsl:otherwise>0</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="string-length($content) + $lineends - $indents"/>
+</xsl:template>
+
+<!-- Almost Nop -->
+<xsl:template match="x:span">
+  <xsl:choose>
+    <xsl:when test="@x:lang and $prettyprint-class!=''">
+      <code class="{$prettyprint-class}">
+        <xsl:call-template name="copy-anchor"/>
+        <xsl:apply-templates/>
+      </code>
+    </xsl:when>
+    <xsl:otherwise>
+      <span>
+        <xsl:call-template name="copy-anchor"/>
+        <xsl:apply-templates/>
+      </span>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:parse-xml">
+  <xsl:apply-templates/>
+
+  <xsl:if test="function-available('exslt:node-set')">
+    <xsl:variable name="cleaned">
+      <xsl:apply-templates mode="cleanup-edits"/>
+    </xsl:variable>
+    <xsl:if test="$xml2rfc-ext-trace-parse-xml='yes'">
+      <xsl:call-template name="trace">
+        <xsl:with-param name="msg" select="concat('Parsing XML: ', $cleaned)"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="function-available('myns:parseXml')" use-when="function-available('myns:parseXml')">
+        <xsl:if test="myns:parseXml(concat($cleaned,''))!=''">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('Parse error in XML: ', myns:parseXml(concat($cleaned,'')))"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:when>
+      <xsl:when test="function-available('saxon:parse')" use-when="function-available('saxon:parse')">
+        <xsl:variable name="parsed" select="saxon:parse(concat($cleaned,''))"/>
+        <xsl:if test="$parsed='foo'">
+          <xsl:comment>should not get here</xsl:comment>
+        </xsl:if>
+      </xsl:when>
+      <xsl:when test="false()"></xsl:when>
+      <xsl:otherwise></xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+</xsl:template>
+
+<!-- inlined RDF support -->
+<xsl:template match="rdf:Description">
+  <!-- ignore -->
+</xsl:template>
+
+<!-- cleanup for ins/del -->
+
+<xsl:template match="comment()|@*" mode="cleanup-edits"><xsl:copy/></xsl:template>
+
+<xsl:template match="text()" mode="cleanup-edits"><xsl:copy/></xsl:template>
+
+<xsl:template match="/" mode="cleanup-edits">
+  <xsl:copy><xsl:apply-templates select="node()" mode="cleanup-edits" /></xsl:copy>
+</xsl:template>
+
+<xsl:template match="ed:del" mode="cleanup-edits"/>
+
+<xsl:template match="ed:replace" mode="cleanup-edits">
+  <xsl:apply-templates mode="cleanup-edits"/>
+</xsl:template>
+
+<xsl:template match="ed:ins" mode="cleanup-edits">
+  <xsl:apply-templates mode="cleanup-edits"/>
+</xsl:template>
+
+
+<!-- ABNF support -->
+<xsl:template name="to-abnf-char-sequence">
+  <xsl:param name="chars"/>
+
+  <xsl:variable name="asciistring">&#160; !"#$%&amp;'()*+,-./<xsl:value-of select="$digits"/>:;&lt;=>?@<xsl:value-of select="$ucase"/>[\]^_`<xsl:value-of select="$lcase"/>{|}~&#127;</xsl:variable> 
+  <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
+  
+  <xsl:variable name="c" select="substring($chars,1,1)"/>
+  <xsl:variable name="r" select="substring($chars,2)"/>
+  <xsl:variable name="pos" select="string-length(substring-before($asciistring,$c))"/>
+  
+  <xsl:choose>
+    <xsl:when test="$pos >= 1">
+      <xsl:variable name="ascii" select="$pos + 31"/>
+      <xsl:variable name="h" select="floor($ascii div 16)"/>
+      <xsl:variable name="l" select="floor($ascii mod 16)"/>
+      <xsl:value-of select="concat(substring($hex,1 + $h,1),substring($hex,1 + $l,1))"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>??</xsl:text>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('unexpected character in ABNF char sequence: ',substring($chars,1,1))" />
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <xsl:if test="$r!=''">
+    <xsl:text>.</xsl:text>
+    <xsl:call-template name="to-abnf-char-sequence">
+      <xsl:with-param name="chars" select="$r"/>
+    </xsl:call-template>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template match="x:abnf-char-sequence">
+  <xsl:choose>
+    <xsl:when test="substring(.,1,1) != '&quot;' or substring(.,string-length(.),1) != '&quot;'">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'contents of x:abnf-char-sequence needs to be quoted.'" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>%x</xsl:text>
+      <xsl:call-template name="to-abnf-char-sequence">
+        <xsl:with-param name="chars" select="substring(.,2,string-length(.)-2)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- box drawing -->
+
+<!-- nop for alignment -->
+<xsl:template match="x:x"/>
+
+<!-- box (top) -->
+<xsl:template match="x:bt">
+  <xsl:text>&#x250c;</xsl:text>
+  <xsl:value-of select="translate(substring(.,2,string-length(.)-2),'-','&#x2500;')"/>
+  <xsl:text>&#x2510;</xsl:text>
+</xsl:template>
+
+<!-- box (center) -->
+<xsl:template match="x:bc">
+  <xsl:variable name="first" select="substring(.,1)"/>
+  <xsl:variable name="content" select="substring(.,2,string-length(.)-2)"/>
+  <xsl:variable name="is-delimiter" select="translate($content,'-','')=''"/>
+
+  <xsl:choose>
+    <xsl:when test="$is-delimiter">
+      <xsl:text>&#x251c;</xsl:text>
+      <xsl:value-of select="translate($content,'-','&#x2500;')"/>
+      <xsl:text>&#x2524;</xsl:text>
+    </xsl:when>
+    <xsl:when test="*">
+      <xsl:for-each select="*|text()">
+        <xsl:choose>
+          <xsl:when test="position()=1">
+            <xsl:text>&#x2502;</xsl:text>
+            <xsl:value-of select="substring(.,2)"/>
+          </xsl:when>
+          <xsl:when test="position()=last()">
+            <xsl:value-of select="substring(.,1,string-length(.)-1)"/>
+            <xsl:text>&#x2502;</xsl:text>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates select="."/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>&#x2502;</xsl:text>
+      <xsl:value-of select="$content"/>
+      <xsl:text>&#x2502;</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<!-- box (bottom) -->
+<xsl:template match="x:bb">
+  <xsl:text>&#x2514;</xsl:text>
+  <xsl:value-of select="translate(substring(.,2,string-length(.)-2),'-','&#x2500;')"/>
+  <xsl:text>&#x2518;</xsl:text>
+</xsl:template>
+
+<!-- author handling extensions -->
+<xsl:template match="x:include-author">
+  <xsl:for-each select="/*/front/author[@anchor=current()/@target]">
+    <xsl:apply-templates select="."/>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- boilerplate -->
+<xsl:template match="boilerplate">
+  <xsl:apply-templates/>
+</xsl:template>
+
+<!-- experimental annotation support -->
+
+<xsl:template match="ed:issueref">
+  <xsl:choose>
+    <xsl:when test=".=//ed:issue/@name">
+      <a href="#{$anchor-pref}issue.{.}">
+        <xsl:apply-templates/>
+      </a>
+    </xsl:when>
+    <xsl:when test="@href">
+      <a href="{@href}" id="{$anchor-pref}issue.{.}">
+        <xsl:apply-templates/>
+      </a>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Dangling ed:issueref: <xsl:value-of select="."/></xsl:with-param>
+      </xsl:call-template>
+      <xsl:apply-templates/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="ed:issue">
+  <xsl:variable name="class">
+    <xsl:choose>
+      <xsl:when test="@status='closed'">closedissue</xsl:when>
+      <xsl:otherwise>openissue</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <table class="{$class}">
+    <tr>
+      <td colspan="3">
+        <a id="{$anchor-pref}issue.{@name}">
+          <xsl:choose>
+            <xsl:when test="@status='closed'">
+              <xsl:attribute name="class">closed-issue</xsl:attribute>
+            </xsl:when>
+            <xsl:when test="@status='editor'">
+              <xsl:attribute name="class">editor-issue</xsl:attribute>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:attribute name="class">open-issue</xsl:attribute>
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>&#160;I&#160;</xsl:text>
+        </a>
+        <xsl:text>&#160;</xsl:text>
+        <xsl:choose>
+          <xsl:when test="@href">
+            <em><a href="{@href}"><xsl:value-of select="@name" /></a></em>
+          </xsl:when>
+          <xsl:when test="@alternate-href">
+            <em>[<a href="{@alternate-href}">alternate link</a>]</em>
+          </xsl:when>
+          <xsl:otherwise>
+            <em><xsl:value-of select="@name" /></em>
+          </xsl:otherwise>
+        </xsl:choose>
+        &#0160;
+        (type: <xsl:value-of select="@type"/>, status: <xsl:value-of select="@status"/>)
+      </td>
+    </tr>
+
+    <xsl:apply-templates select="ed:item"/>
+    <xsl:apply-templates select="ed:resolution"/>
+
+    <xsl:variable name="changes" select="//*[@ed:resolves=current()/@name or ed:resolves=current()/@name]" />
+    <xsl:if test="$changes">
+      <tr>
+        <td class="top" colspan="3">
+          Associated changes in this document:
+          <xsl:variable name="issue" select="@name"/>
+          <xsl:for-each select="$changes">
+            <a href="#{$anchor-pref}change.{$issue}.{position()}">
+              <xsl:variable name="label">
+                <xsl:call-template name="get-section-number"/>
+              </xsl:variable>
+              <xsl:choose>
+                <xsl:when test="$label!=''"><xsl:value-of select="$label"/></xsl:when>
+                <xsl:otherwise>&lt;<xsl:value-of select="concat('#',$anchor-pref,'change.',$issue,'.',position())"/>&gt;</xsl:otherwise>
+              </xsl:choose>
+            </a>
+            <xsl:if test="position()!=last()">, </xsl:if>
+          </xsl:for-each>
+          <xsl:text>.</xsl:text>
+        </td>
+      </tr>
+    </xsl:if>
+  </table>
+
+</xsl:template>
+
+<xsl:template match="ed:item">
+  <tr>
+    <td class="top">
+      <xsl:if test="@entered-by">
+        <a href="mailto:{@entered-by}?subject={/rfc/@docName},%20{../@name}">
+          <i><xsl:value-of select="@entered-by"/></i>
+        </a>
+      </xsl:if>
+    </td>
+    <td class="topnowrap">
+      <xsl:value-of select="@date"/>
+    </td>
+    <td class="top">
+      <xsl:apply-templates select="node()" mode="issuehtml"/>
+    </td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="ed:resolution">
+  <tr>
+    <td class="top">
+      <xsl:if test="@entered-by">
+        <a href="mailto:{@entered-by}?subject={/rfc/@docName},%20{../@name}"><i><xsl:value-of select="@entered-by"/></i></a>
+      </xsl:if>
+    </td>
+    <td class="topnowrap">
+      <xsl:value-of select="@datetime"/>
+    </td>
+    <td class="top">
+      <em>Resolution:</em>
+      <xsl:apply-templates select="node()" mode="issuehtml"/>
+    </td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="ed:annotation">
+  <em>
+    <xsl:apply-templates/>
+  </em>
+</xsl:template>
+
+<!-- special templates for handling XHTML in issues -->
+<xsl:template match="text()" mode="issuehtml">
+  <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template match="*|@*" mode="issuehtml">
+  <xsl:message terminate="yes">Unexpected node in issue HTML: <xsl:value-of select="name(.)"/></xsl:message>
+</xsl:template>
+
+<xsl:template match="xhtml:a|xhtml:b|xhtml:br|xhtml:cite|xhtml:del|xhtml:em|xhtml:i|xhtml:ins|xhtml:q|xhtml:pre|xhtml:tt" mode="issuehtml">
+  <xsl:element name="{local-name()}">
+    <xsl:apply-templates select="@*|node()" mode="issuehtml"/>
+  </xsl:element>
+</xsl:template>
+
+<xsl:template match="xhtml:p" mode="issuehtml">
+  <xsl:apply-templates select="node()" mode="issuehtml"/>
+  <br class="p"/>
+</xsl:template>
+
+<xsl:template match="xhtml:a/@href|xhtml:q/@cite" mode="issuehtml">
+  <xsl:attribute name="{local-name(.)}">
+    <xsl:value-of select="."/>
+  </xsl:attribute>
+</xsl:template>
+
+<xsl:template match="ed:issueref" mode="issuehtml">
+  <xsl:apply-templates select="."/>
+</xsl:template>
+
+<xsl:template match="ed:eref" mode="issuehtml">
+  <xsl:text>&lt;</xsl:text>
+  <a href="{.}"><xsl:value-of select="."/></a>
+  <xsl:text>&gt;</xsl:text>
+</xsl:template>
+
+<xsl:template name="insertIssuesList">
+
+  <h2 id="{$anchor-pref}issues-list" ><a href="#{$anchor-pref}issues-list">Issues list</a></h2>
+  <table>
+    <thead>
+      <tr>
+        <th>Id</th>
+        <th>Type</th>
+        <th>Status</th>
+        <th>Date</th>
+        <th>Raised By</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="//ed:issue">
+        <xsl:sort select="@status" />
+        <xsl:sort select="@name" />
+        <tr>
+          <td><a href="#{$anchor-pref}issue.{@name}"><xsl:value-of select="@name" /></a></td>
+          <td><xsl:value-of select="@type" /></td>
+          <td><xsl:value-of select="@status" /></td>
+          <td><xsl:value-of select="ed:item[1]/@date" /></td>
+          <td><a href="mailto:{ed:item[1]/@entered-by}?subject={/rfc/@docName},%20{@name}"><xsl:value-of select="ed:item[1]/@entered-by" /></a></td>
+        </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+
+</xsl:template>
+
+<xsl:template name="insert-diagnostics">
+
+  <!-- check anchor names -->
+
+  <xsl:variable name="badAnchors" select="//*[starts-with(@anchor,$anchor-pref)]" />
+  <xsl:if test="$badAnchors">
+    <xsl:variable name="text">
+      <xsl:text>The following anchor names may collide with internally generated anchors because of their prefix "</xsl:text>
+      <xsl:value-of select="$anchor-pref" />
+      <xsl:text>": </xsl:text>
+      <xsl:for-each select="$badAnchors">
+        <xsl:value-of select="@anchor"/>
+        <xsl:call-template name="lineno"/>
+        <xsl:if test="position()!=last()">, </xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg"><xsl:value-of select="normalize-space($text)"/></xsl:with-param>
+      <xsl:with-param name="lineno" select="false()"/>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="badV3Anchors" select="//*[substring(@anchor,2,1)='-' and translate(substring(@anchor,1,1),$lcase,'')='']" />
+  <xsl:if test="$badV3Anchors">
+    <xsl:variable name="text">
+      <xsl:text>The following anchor names may collide with internally generated anchors in XML2RFCV3 mode because: </xsl:text>
+      <xsl:for-each select="$badV3Anchors">
+        <xsl:value-of select="@anchor"/>
+        <xsl:call-template name="lineno"/>
+        <xsl:if test="position()!=last()">, </xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg"><xsl:value-of select="normalize-space($text)"/></xsl:with-param>
+      <xsl:with-param name="lineno" select="false()"/>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="all-refs" select="/rfc/back/references/reference|exslt:node-set($includeDirectives)//reference|exslt:node-set($sourcedReferences)//reference"/>
+
+  <!-- check ABNF syntax references -->
+  <xsl:if test="//artwork[@type='abnf2616' or @type='abnf7230']|//sourcecode[@type='abnf2616' or type='abnf7320']">
+    <xsl:if test="not($all-refs//seriesInfo[@name='RFC' and (@value='2068' or @value='2616' or @value='7230')]) and not($all-refs//seriesInfo[@name='Internet-Draft' and (starts-with(@value, 'draft-ietf-httpbis-p1-messaging-') or starts-with(@value, 'draft-ietf-httpbis-semantics-'))])">
+      <!-- check for draft-ietf-httpbis-p1-messaging- is for backwards compat -->
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">document uses HTTP-style ABNF syntax, but doesn't reference RFC 2068, RFC 2616, or RFC 7230.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="//artwork[@type='abnf']|//sourcecode[@type='abnf']">
+    <xsl:if test="not($all-refs//seriesInfo[@name='RFC' and (@value='2234' or @value='4234' or @value='5234')])">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">document uses ABNF syntax, but doesn't reference RFC 2234, 4234 or 5234.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <!-- check IDs -->
+  <xsl:variable name="badTargets" select="//xref[not(ancestor::toc)][not(@target=//@anchor) and not(@target=exslt:node-set($includeDirectives)//@anchor) and not(ancestor::ed:del)]" />
+  <xsl:if test="$badTargets">
+    <xsl:variable name="text">
+      <xsl:text>The following target names do not exist: </xsl:text>
+      <xsl:for-each select="$badTargets">
+        <xsl:value-of select="@target"/>
+        <xsl:if test="not(@target)">(@target attribute missing)</xsl:if>
+        <xsl:call-template name="lineno"/>
+        <xsl:if test="position()!=last()">
+          <xsl:text>, </xsl:text>
+        </xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg"><xsl:value-of select="$text"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+
+</xsl:template>
+
+<!-- special change mark support, not supported by RFC2629 yet -->
+
+<xsl:template match="@ed:*" />
+
+<xsl:template match="ed:del">
+  <xsl:call-template name="insert-issue-pointer"/>
+  <del>
+    <xsl:copy-of select="@*[namespace-uri()='']"/>
+    <xsl:if test="not(@title) and ancestor-or-self::*[@ed:entered-by] and @datetime">
+      <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates />
+  </del>
+</xsl:template>
+
+<xsl:template match="ed:ins">
+  <xsl:call-template name="insert-issue-pointer"/>
+  <ins>
+    <xsl:copy-of select="@*[namespace-uri()='']"/>
+    <xsl:if test="not(@title) and ancestor-or-self::*[@ed:entered-by] and @datetime">
+      <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates />
+  </ins>
+</xsl:template>
+
+<xsl:template name="insert-issue-pointer">
+  <xsl:param name="deleted-anchor"/>
+  <xsl:variable name="change" select="."/>
+  <xsl:for-each select="@ed:resolves|ed:resolves">
+    <xsl:variable name="resolves" select="."/>
+    <!-- need the right context node for proper numbering -->
+    <xsl:variable name="count"><xsl:for-each select=".."><xsl:number level="any" count="*[@ed:resolves=$resolves or ed:resolves=$resolves]" /></xsl:for-each></xsl:variable>
+    <xsl:variable name="total" select="count(//*[@ed:resolves=$resolves or ed:resolves=$resolves])" />
+    <xsl:variable name="id">
+      <xsl:value-of select="$anchor-pref"/>change.<xsl:value-of select="$resolves"/>.<xsl:value-of select="$count" />
+    </xsl:variable>
+    <xsl:choose>
+      <!-- block level? -->
+      <xsl:when test="not(ancestor::t) and not(ancestor::title) and not(ancestor::figure) and not($change/@ed:old-title)">
+        <div class="issuepointer {$css-noprint}">
+          <xsl:if test="not($deleted-anchor)">
+            <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+          </xsl:if>
+          <xsl:if test="$count > 1">
+            <a class="bg-issue" title="previous change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count - 1}">&#x2191;</a>
+          </xsl:if>
+          <a class="open-issue" href="#{$anchor-pref}issue.{$resolves}" title="resolves: {$resolves}">
+            <xsl:choose>
+              <xsl:when test="//ed:issue[@name=$resolves and @status='closed']">
+                <xsl:attribute name="class">closed-issue</xsl:attribute>
+              </xsl:when>
+              <xsl:when test="//ed:issue[@name=$resolves and @status='editor']">
+                <xsl:attribute name="class">editor-issue</xsl:attribute>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:attribute name="class">open-issue</xsl:attribute>
+              </xsl:otherwise>
+            </xsl:choose>
+            <xsl:text>&#160;I&#160;</xsl:text>
+          </a>
+          <xsl:if test="$count &lt; $total">
+            <a class="bg-issue" title="next change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count + 1}">&#x2193;</a>
+          </xsl:if>
+          <xsl:text>&#160;</xsl:text>
+        </div>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:if test="$count > 1">
+          <a class="bg-issue" title="previous change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count - 1}">&#x2191;</a>
+        </xsl:if>
+        <a title="resolves: {$resolves}" href="#{$anchor-pref}issue.{$resolves}">
+          <xsl:if test="not($deleted-anchor)">
+            <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+          </xsl:if>
+          <xsl:choose>
+            <xsl:when test="//ed:issue[@name=$resolves and @status='closed']">
+              <xsl:attribute name="class">closed-issue <xsl:value-of select="$css-noprint"/></xsl:attribute>
+            </xsl:when>
+            <xsl:when test="//ed:issue[@name=$resolves and @status='editor']">
+              <xsl:attribute name="class">editor-issue <xsl:value-of select="$css-noprint"/></xsl:attribute>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:attribute name="class">open-issue <xsl:value-of select="$css-noprint"/></xsl:attribute>
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>&#160;I&#160;</xsl:text>
+        </a>
+        <xsl:if test="$count &lt; $total">
+          <a class="bg-issue" title="next change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count + 1}">&#x2193;</a>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="ed:replace">
+  <!-- we need to special-case things like lists and tables -->
+  <xsl:choose>
+    <xsl:when test="parent::list">
+      <xsl:apply-templates select="ed:del/node()" />
+      <xsl:apply-templates select="ed:ins/node()" />
+    </xsl:when>
+    <xsl:when test="parent::references">
+      <xsl:apply-templates select="ed:del/node()" />
+      <xsl:apply-templates select="ed:ins/node()" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:if test="@cite">
+        <a class="editor-issue" href="{@cite}" target="_blank" title="see {@cite}">
+          <xsl:text>&#160;i&#160;</xsl:text>
+        </a>
+      </xsl:if>
+      <xsl:call-template name="insert-issue-pointer"/>
+      <xsl:if test="ed:del">
+        <del>
+          <xsl:copy-of select="@*[namespace-uri()='']"/>
+          <xsl:if test="not(@title) and ancestor-or-self::xsl:template[@ed:entered-by] and @datetime">
+            <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+          </xsl:if>
+          <xsl:apply-templates select="ed:del/node()" />
+        </del>
+      </xsl:if>
+      <xsl:if test="ed:ins">
+        <ins>
+          <xsl:copy-of select="@*[namespace-uri()='']"/>
+          <xsl:if test="not(@title) and ancestor-or-self::*[@ed:entered-by] and @datetime">
+            <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+          </xsl:if>
+          <xsl:apply-templates select="ed:ins/node()" />
+        </ins>
+      </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- convenience template for helping Mozilla (pre/ins inheritance problem) -->
+<xsl:template name="insertInsDelClass">
+  <xsl:if test="ancestor::ed:del">
+    <xsl:attribute name="class">del</xsl:attribute>
+  </xsl:if>
+  <xsl:if test="ancestor::ed:ins">
+    <xsl:attribute name="class">ins</xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+
+<xsl:template name="sectionnumberAndEdits">
+  <xsl:choose>
+    <xsl:when test="ancestor::ed:del">
+      <xsl:text>del-</xsl:text>
+      <xsl:number count="ed:del//section" level="any"/>
+    </xsl:when>
+    <xsl:when test="@x:fixed-section-number and @x:fixed-section-number!=''">
+      <xsl:value-of select="@x:fixed-section-number"/>
+    </xsl:when>
+    <xsl:when test="(@x:fixed-section-number and @x:fixed-section-number='') or @numbered='false'">
+      <xsl:value-of select="$unnumbered"/>
+      <xsl:number count="section[@x:fixed-section-number='' or @numbered='false']" level="any"/>
+    </xsl:when>
+    <xsl:when test="self::section and parent::ed:ins and local-name(../..)='replace'">
+      <xsl:for-each select="../.."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+      <xsl:for-each select="..">
+        <xsl:if test="parent::ed:replace">
+          <xsl:for-each select="..">
+            <xsl:if test="parent::section">.</xsl:if>
+            <xsl:variable name="cnt" select="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+            <xsl:choose>
+              <xsl:when test="ancestor::back and not(ancestor::section)"><xsl:number format="A" value="$cnt"/></xsl:when>
+              <xsl:otherwise><xsl:value-of select="$cnt"/></xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </xsl:if>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="self::section[parent::ed:ins]">
+      <xsl:for-each select="../.."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+      <xsl:for-each select="..">
+        <xsl:if test="parent::section">.</xsl:if><xsl:value-of select="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="self::section">
+      <xsl:for-each select=".."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+      <xsl:if test="parent::section">.</xsl:if>
+      <xsl:choose>
+        <xsl:when test="parent::back">
+          <xsl:number format="A" value="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:number value="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="self::references">
+      <xsl:choose>
+        <xsl:when test="count(/*/back/references)+count(/*/back/ed:replace/ed:ins/references)=1"><xsl:call-template name="get-references-section-number"/></xsl:when>
+        <xsl:otherwise><xsl:call-template name="get-references-section-number"/>.<xsl:number level="any"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="self::middle or self::back"><!-- done --></xsl:when>
+    <xsl:otherwise>
+      <!-- go up one level -->
+      <xsl:for-each select=".."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- utilities for warnings -->
+
+<xsl:template name="trace">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="inline" select="'no'"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">TRACE</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="$inline"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="inline-warning">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">WARNING</xsl:with-param>
+    <xsl:with-param name="dlevel">3</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="'yes'"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="warning">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">WARNING</xsl:with-param>
+    <xsl:with-param name="dlevel">3</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="'no'"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="info">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">INFO</xsl:with-param>
+    <xsl:with-param name="dlevel">2</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="'no'"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="error">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="inline"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">ERROR</xsl:with-param>
+    <xsl:with-param name="dlevel">4</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="$inline"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-message-inline">
+  <xsl:param name="message"/>
+  <xsl:choose>
+    <xsl:when test="ancestor::t or ancestor-or-self::seriesInfo">
+      <span class="{$css-error}"><xsl:value-of select="$message"/></span>
+    </xsl:when>
+    <xsl:otherwise>
+      <div class="{$css-error}"><xsl:value-of select="$message"/></div>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="emit-message">
+  <xsl:param name="level">DEBUG</xsl:param>
+  <xsl:param name="dlevel">0</xsl:param>
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="inline"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:if test="$dlevel >= $log-level">
+    <xsl:variable name="message"><xsl:value-of select="$level"/>: <xsl:value-of select="$msg"/><xsl:if test="$msg2!=''"> - <xsl:value-of select="$msg2"/></xsl:if><xsl:if test="$lineno"><xsl:call-template name="lineno"/></xsl:if></xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$inline!='no'">
+        <xsl:call-template name="emit-message-inline">
+          <xsl:with-param name="message" select="$message"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- this fails when the message contains characters not encodable in the output encoding -->
+        <!-- <xsl:comment><xsl:value-of select="$message"/></xsl:comment> -->
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="$dlevel >= $abort-log-level">
+        <xsl:message terminate="yes"><xsl:value-of select="$message"/></xsl:message>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:message><xsl:value-of select="$message"/></xsl:message>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+</xsl:template>
+
+<!-- table formatting -->
+
+<xsl:template match="table">
+  <div class="{$css-tt}">
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="iref"/>
+    <xsl:variable name="style">
+      <xsl:text>v3 </xsl:text>
+      <xsl:choose>
+        <xsl:when test="@align='left'"><xsl:value-of select="$css-tleft"/></xsl:when>
+        <xsl:when test="@align='right'"><xsl:value-of select="$css-tright"/></xsl:when>
+        <xsl:when test="@align='center' or not(@align) or @align=''"><xsl:value-of select="$css-tcenter"/></xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:variable>
+
+    <table class="{$style}">
+      <xsl:variable name="n"><xsl:call-template name="get-table-number"/></xsl:variable>
+      <caption>
+        <xsl:text>Table </xsl:text>
+        <xsl:value-of select="$n"/>
+        <xsl:if test="name">
+          <xsl:text>: </xsl:text>
+          <xsl:apply-templates select="name/node()"/>
+        </xsl:if>
+      </caption>
+      <xsl:apply-templates select="*[not(self::iref)]"/>
+    </table>
+  </div>
+</xsl:template>
+
+<xsl:template match="table/name"/>
+
+<xsl:template match="tbody">
+  <tbody>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </tbody>
+</xsl:template>
+
+<xsl:template match="tfoot">
+  <tfoot>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </tfoot>
+</xsl:template>
+
+<xsl:template match="thead">
+  <thead>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </thead>
+</xsl:template>
+
+<xsl:template match="tr">
+  <tr>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </tr>
+</xsl:template>
+
+<xsl:template name="t-alignment">
+  <xsl:attribute name="class">
+    <xsl:choose>
+      <xsl:when test="@align='left' or not(@align) or @align=''"><xsl:value-of select="$css-left"/></xsl:when>
+      <xsl:when test="@align='right'"><xsl:value-of select="$css-right"/></xsl:when>
+      <xsl:when test="@align='center'"><xsl:value-of select="$css-center"/></xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">Unknown align attribute: <xsl:value-of select="@align"/></xsl:with-param>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:attribute>
+</xsl:template>
+
+<xsl:template match="td">
+  <td>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="t-alignment"/>
+    <xsl:copy-of select="@colspan|@rowspan"/>
+    <xsl:apply-templates select="node()"/>
+  </td>
+</xsl:template>
+
+<xsl:template match="th">
+  <th>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="t-alignment"/>
+    <xsl:copy-of select="@colspan|@rowspan"/>
+    <xsl:apply-templates select="node()"/>
+  </th>
+</xsl:template>
+
+<xsl:template match="texttable">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:variable name="anch">
+    <xsl:call-template name="get-table-anchor"/>
+  </xsl:variable>
+
+  <div id="{$anch}" class="{$css-tt}">
+
+    <xsl:if test="@anchor!=''">
+      <div id="{@anchor}"/>
+    </xsl:if>
+    <xsl:apply-templates select="preamble" />
+
+    <xsl:variable name="style">
+      <xsl:value-of select="$css-tt"/>
+      <xsl:text> </xsl:text>
+      <xsl:choose>
+        <xsl:when test="@style!=''">
+          <xsl:value-of select="@style"/>
+        </xsl:when>
+        <xsl:otherwise>full</xsl:otherwise>
+      </xsl:choose>
+      <xsl:choose>
+        <xsl:when test="@align='left'"><xsl:text> </xsl:text><xsl:value-of select="$css-tleft"/></xsl:when>
+        <xsl:when test="@align='right'"><xsl:text> </xsl:text><xsl:value-of select="$css-tright"/></xsl:when>
+        <xsl:when test="@align='center' or not(@align) or @align=''"><xsl:text> </xsl:text><xsl:value-of select="$css-tcenter"/></xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:variable>
+
+    <table class="{$style}">
+      <xsl:if test="(@title!='') or (@anchor!='' and not(@suppress-title='true'))">
+        <xsl:variable name="n"><xsl:call-template name="get-table-number"/></xsl:variable>
+        <caption>
+          <xsl:if test="@x:caption-side='top'">
+            <xsl:attribute name="class">caption-top</xsl:attribute>
+          </xsl:if>
+          <xsl:if test="not(starts-with($n,'u'))">
+            <xsl:text>Table </xsl:text>
+            <xsl:value-of select="$n"/>
+            <xsl:if test="@title!=''">: </xsl:if>
+          </xsl:if>
+          <xsl:if test="@title!=''">
+            <xsl:value-of select="@title" />
+          </xsl:if>
+        </caption>
+      </xsl:if>
+
+      <xsl:if test="ttcol!=''">
+        <!-- skip header when all column titles are empty -->
+        <thead>
+          <tr>
+            <xsl:apply-templates select="ttcol" />
+          </tr>
+        </thead>
+      </xsl:if>
+      <tbody>
+        <xsl:variable name="columns" select="count(ttcol)" />
+        <xsl:variable name="fields" select="c | ed:replace/ed:ins/c | ed:replace/ed:del/c" />
+        <xsl:for-each select="$fields[$columns=1 or (position() mod $columns) = 1]">
+          <tr>
+            <xsl:for-each select=". | following-sibling::c[position() &lt; $columns]">
+              <td>
+                <xsl:call-template name="copy-anchor"/>
+                <xsl:call-template name="insertInsDelClass"/>
+                <xsl:variable name="pos" select="position()" />
+                <xsl:variable name="col" select="../ttcol[position() = $pos]" />
+                <xsl:choose>
+                  <xsl:when test="$col/@align='right' or $col/@align='center'">
+                    <xsl:attribute name="class"><xsl:value-of select="$col/@align"/></xsl:attribute>
+                  </xsl:when>
+                  <xsl:when test="$col/@align='left' or not($col/@align)">
+                    <xsl:attribute name="class"><xsl:value-of select="$css-left"/></xsl:attribute>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:call-template name="warning">
+                      <xsl:with-param name="msg">Unknown align attribute on ttcol: <xsl:value-of select="$col/@align"/></xsl:with-param>
+                    </xsl:call-template>
+                  </xsl:otherwise>
+                </xsl:choose>
+                <xsl:apply-templates select="node()" />
+              </td>
+            </xsl:for-each>
+          </tr>
+        </xsl:for-each>
+      </tbody>
+    </table>
+    <xsl:apply-templates select="postamble" />
+  </div>
+
+</xsl:template>
+
+<xsl:template match="ttcol">
+  <th>
+
+    <xsl:choose>
+      <xsl:when test="@align='right' or @align='center' or @align='left'">
+        <xsl:attribute name="class"><xsl:value-of select="@align"/></xsl:attribute>
+      </xsl:when>
+      <xsl:when test="not(@align)">
+        <!-- that's the default, nothing to do here -->
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:message>Unknown align attribute on ttcol: <xsl:value-of select="@align"/></xsl:message>
+      </xsl:otherwise>
+    </xsl:choose>
+
+    <xsl:if test="@width">
+      <xsl:attribute name="style">width: <xsl:value-of select="@width" />;</xsl:attribute>
+    </xsl:if>
+
+    <xsl:apply-templates />
+  </th>
+</xsl:template>
+
+<!-- cref support -->
+
+<xsl:template name="get-comment-name">
+  <xsl:choose>
+    <xsl:when test="@anchor">
+      <xsl:value-of select="@anchor"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$anchor-pref"/>
+      <xsl:text>comment.</xsl:text>
+      <xsl:number count="cref[not(@anchor)]" level="any"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="cref[@display='false']">
+  <!-- hidden -->
+</xsl:template>
+
+<xsl:template match="cref[not(@display) or @display!='false']">
+  <xsl:if test="$xml2rfc-comments!='no'">
+    <xsl:variable name="cid">
+      <xsl:call-template name="get-comment-name"/>
+    </xsl:variable>
+
+    <span class="comment">
+      <xsl:choose>
+        <xsl:when test="$xml2rfc-inline='yes'">
+          <xsl:attribute name="id">
+            <xsl:value-of select="$cid"/>
+          </xsl:attribute>
+          <xsl:text>[</xsl:text>
+          <xsl:if test="@anchor or (not(/rfc/@version) or /rfc/@version &lt; 3)">
+            <a href="#{$cid}" class="smpl">
+              <xsl:value-of select="$cid"/>
+            </a>
+            <xsl:text>: </xsl:text>
+          </xsl:if>
+          <xsl:apply-templates select="text()|eref|xref"/>
+          <xsl:if test="@source"> --<xsl:value-of select="@source"/></xsl:if>
+          <xsl:text>]</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:attribute name="title">
+            <xsl:if test="@source"><xsl:value-of select="@source"/>: </xsl:if>
+            <xsl:variable name="content">
+              <xsl:apply-templates select="text()|eref|xref"/>
+            </xsl:variable>
+            <xsl:value-of select="$content"/>
+          </xsl:attribute>
+          <xsl:text>[</xsl:text>
+          <a href="#{$cid}">
+            <xsl:value-of select="$cid"/>
+          </a>
+          <xsl:text>]</xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+    </span>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insertComments">
+
+  <xsl:call-template name="insert-conditional-hrule"/>
+
+  <h2>
+    <xsl:call-template name="insert-conditional-pagebreak"/>
+    <a id="{$anchor-pref}comments" href="#{$anchor-pref}comments">Editorial Comments</a>
+  </h2>
+
+  <dl>
+    <xsl:for-each select="//cref[not(@display) or @display!='false']">
+      <xsl:variable name="cid">
+        <xsl:choose>
+          <xsl:when test="@anchor">
+            <xsl:value-of select="@anchor"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$anchor-pref"/>
+            <xsl:text>comment.</xsl:text>
+            <xsl:number count="cref[not(@anchor)]" level="any"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <dt id="{$cid}">
+        [<xsl:value-of select="$cid"/>]
+      </dt>
+      <dd>
+        <xsl:apply-templates select="node()"/>
+        <xsl:if test="@source"> --<xsl:value-of select="@source"/></xsl:if>
+      </dd>
+    </xsl:for-each>
+  </dl>
+</xsl:template>
+
+
+<!-- Chapter Link Generation -->
+
+<xsl:template match="*" mode="links"><xsl:apply-templates mode="links"/></xsl:template>
+<xsl:template match="text()" mode="links" />
+
+<xsl:template match="/*/middle//section[not(ancestor::section)]" mode="links">
+  <xsl:variable name="sectionNumber"><xsl:call-template name="get-section-number" /></xsl:variable>
+  <xsl:variable name="title">
+    <xsl:if test="$sectionNumber!='' and not(contains($sectionNumber,$unnumbered))">
+      <xsl:value-of select="$sectionNumber"/>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="name">
+        <xsl:variable name="hold">
+          <xsl:apply-templates select="name/node()"/>
+        </xsl:variable>
+        <xsl:value-of select="normalize-space($hold)"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <link rel="Chapter" title="{$title}" href="#{$anchor-pref}section.{$sectionNumber}"/>
+  <xsl:apply-templates mode="links" />
+</xsl:template>
+
+<xsl:template match="/*/back//section[not(ancestor::section)]" mode="links">
+  <xsl:variable name="sectionNumber"><xsl:call-template name="get-section-number" /></xsl:variable>
+  <xsl:variable name="title">
+    <xsl:if test="$sectionNumber!='' and not(contains($sectionNumber,$unnumbered))">
+      <xsl:value-of select="$sectionNumber"/>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="name">
+        <xsl:variable name="hold">
+          <xsl:apply-templates select="name/node()"/>
+        </xsl:variable>
+        <xsl:value-of select="normalize-space($hold)"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <link rel="Appendix" title="{$title}" href="#{$anchor-pref}section.{$sectionNumber}"/>
+  <xsl:apply-templates mode="links" />
+</xsl:template>
+
+<xsl:template match="/*/back/references[position()=1]" mode="links">
+  <xsl:variable name="sectionNumber"><xsl:call-template name="get-references-section-number" /></xsl:variable>
+  <xsl:variable name="title">
+    <xsl:choose>
+      <xsl:when test="@title and count(/*/back/references)=1">
+        <xsl:call-template name="get-references-section-number"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="@title"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="get-references-section-number"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="$xml2rfc-refparent"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <link rel="Chapter" title="{$title}" href="#{$anchor-pref}section.{$sectionNumber}"/>
+</xsl:template>
+
+<!-- convenience templates -->
+
+<xsl:template name="get-author-summary">
+  <xsl:choose>
+    <xsl:when test="count(/rfc/front/author)=1">
+      <xsl:value-of select="/rfc/front/author[1]/@surname" />
+    </xsl:when>
+    <xsl:when test="count(/rfc/front/author)=2">
+      <xsl:value-of select="concat(/rfc/front/author[1]/@surname,' &amp; ',/rfc/front/author[2]/@surname)" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat(/rfc/front/author[1]/@surname,', et al.')" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-bottom-center">
+  <xsl:choose>
+    <xsl:when test="/rfc/@docName">
+      <!-- for IDs, use the expiry date -->
+      <xsl:text>Expires </xsl:text><xsl:call-template name="expirydate" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="get-category-long"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-category-long">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-footer!=''"><xsl:value-of select="$xml2rfc-footer" /></xsl:when>
+    <xsl:when test="$xml2rfc-private!=''"/> <!-- private draft, footer not set -->
+    <xsl:when test="/rfc/@category='bcp'">Best Current Practice</xsl:when>
+    <xsl:when test="/rfc/@category='historic'">Historic</xsl:when>
+    <xsl:when test="/rfc/@category='info' or not(/rfc/@category)">Informational</xsl:when>
+    <xsl:when test="/rfc/@category='std'">Standards Track</xsl:when>
+    <xsl:when test="/rfc/@category='exp'">Experimental</xsl:when>
+    <xsl:otherwise>(category unknown)</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-header-center">
+  <xsl:choose>
+    <xsl:when test="string-length(/rfc/front/title/@abbrev) &gt; 0">
+      <xsl:value-of select="/rfc/front/title/@abbrev" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates select="/rfc/front/title" mode="get-text-content" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-header-left">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-header!=''"><xsl:value-of select="$xml2rfc-header" /></xsl:when>
+    <xsl:when test="$xml2rfc-private!=''"/> <!-- private draft, header not set -->
+    <xsl:when test="/rfc/@ipr and not($is-rfc)">Internet-Draft</xsl:when>
+    <xsl:otherwise>RFC <xsl:value-of select="$rfcno"/></xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-generator">
+  <xsl:variable name="gen">
+    <xsl:text>http://greenbytes.de/tech/webdav/rfc2629.xslt, </xsl:text>
+    <!-- when RCS keyword substitution in place, add version info -->
+    <xsl:if test="contains('$Revision: 1.1328 $',':')">
+      <xsl:value-of select="concat('Revision ',normalize-space(translate(substring-after('$Revision: 1.1328 $', 'Revision: '),'$','')),', ')" />
+    </xsl:if>
+    <xsl:if test="contains('$Date: 2020/09/24 11:34:24 $',':')">
+      <xsl:value-of select="concat(normalize-space(translate(substring-after('$Date: 2020/09/24 11:34:24 $', 'Date: '),'$','')),', ')" />
+    </xsl:if>
+    <xsl:variable name="product" select="normalize-space(concat(system-property('xsl:product-name'),' ',system-property('xsl:product-version')))"/>
+    <xsl:if test="$product!=''">
+      <xsl:value-of select="concat('XSLT processor: ',$product,', ')"/>
+    </xsl:if>
+    <xsl:value-of select="concat('XSLT vendor: ',system-property('xsl:vendor'),' ',system-property('xsl:vendor-url'))" />
+  </xsl:variable>
+  <xsl:variable name="via">
+    <xsl:variable name="c1" select="/comment()[starts-with(normalize-space(.),'generated by ')]"/>
+    <xsl:variable name="mmark-lookup">name="GENERATOR" content=</xsl:variable>
+    <xsl:variable name="c2" select="/comment()[starts-with(normalize-space(.),$mmark-lookup)]"/>
+    <xsl:choose>
+      <xsl:when test="$c1">
+        <xsl:value-of select="substring-after(normalize-space($c1),'generated by ')"/>
+      </xsl:when>
+      <xsl:when test="$c2">
+        <xsl:variable name="remove">"</xsl:variable>
+        <xsl:value-of select="translate(substring-after(normalize-space($c2),$mmark-lookup),$remove,'')"/>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="$gen" />
+  <xsl:if test="$via!=''">
+    <xsl:text>, via: </xsl:text>
+    <xsl:value-of select="$via"/>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="get-header-right">
+  <xsl:if test="$xml2rfc-ext-pub-day!='' and /rfc/front/date/@x:include-day='true' and $is-rfc">
+    <xsl:value-of select="number($xml2rfc-ext-pub-day)" />
+    <xsl:text> </xsl:text>
+  </xsl:if>
+  <xsl:value-of select="concat($xml2rfc-ext-pub-month, ' ', $xml2rfc-ext-pub-year)" />
+</xsl:template>
+
+<xsl:template name="get-keywords">
+  <xsl:for-each select="/rfc/front/keyword">
+    <xsl:if test="contains(.,',')">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">keyword element appears to contain a comma-separated list, split into multiple elements instead.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:value-of select="normalize-space(.)" />
+    <xsl:if test="position()!=last()">, </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- get language from context node. nearest ancestor or return the default of "en" -->
+<xsl:template name="get-lang">
+  <xsl:choose>
+    <xsl:when test="ancestor-or-self::*[@xml:lang]"><xsl:value-of select="ancestor-or-self::*/@xml:lang" /></xsl:when>
+    <xsl:otherwise>en</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-section-number">
+  <xsl:variable name="anchor" select="@anchor"/>
+  <xsl:choose>
+    <xsl:when test="self::note">
+      <xsl:number count="note"/>
+    </xsl:when>
+    <xsl:when test="@x:fixed-section-number and @x:fixed-section-number!=''">
+      <xsl:value-of select="@x:fixed-section-number"/>
+    </xsl:when>
+    <xsl:when test="(@x:fixed-section-number and @x:fixed-section-number='') or ancestor-or-self::*/@numbered='false'">
+      <xsl:value-of select="$unnumbered"/>
+      <xsl:number count="section[@x:fixed-section-number='' or ancestor-or-self::*/@numbered='false']" level="any"/>
+      <!-- checks -->
+      <xsl:if test="@numbered='false'">
+        <xsl:if test="ancestor::section or ancestor::section">
+          <xsl:call-template name="error">
+            <xsl:with-param name="inline" select="'no'"/>
+            <xsl:with-param name="msg">Only top-level sections can be unnumbered</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="following-sibling::section[not(@numbered) or @numbered!='false']">
+          <xsl:call-template name="error">
+            <xsl:with-param name="inline" select="'no'"/>
+            <xsl:with-param name="msg">Unnumbered section is followed by numbered sections</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="ancestor::middle and ../../back/references">
+          <xsl:call-template name="error">
+            <xsl:with-param name="inline" select="'no'"/>
+            <xsl:with-param name="msg">Unnumbered section is followed by (numbered) references section</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$has-edits or ancestor::*/@x:fixed-section-number">
+      <xsl:call-template name="sectionnumberAndEdits" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:choose>
+        <xsl:when test="self::references and not(parent::references)">
+          <xsl:choose>
+            <xsl:when test="count(/*/back/references)=1">
+              <xsl:call-template name="get-references-section-number"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:call-template name="get-references-section-number"/>.<xsl:number count="references"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="self::references and parent::references">
+          <xsl:for-each select=".."><xsl:call-template name="get-section-number"/></xsl:for-each>.<xsl:number count="references"/>
+        </xsl:when>
+        <xsl:when test="self::reference">
+          <xsl:for-each select="parent::references">
+            <xsl:choose>
+              <xsl:when test="count(/*/back/references)=1">
+                <xsl:call-template name="get-references-section-number"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:call-template name="get-references-section-number"/>.<xsl:number count="references"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </xsl:when>
+        <xsl:when test="ancestor::reference">
+          <xsl:for-each select="ancestor::reference">
+            <xsl:call-template name="get-section-number"/>
+          </xsl:for-each>
+        </xsl:when>
+        <xsl:when test="ancestor::back"><xsl:number count="section|appendix" level="multiple" format="A.1.1.1.1.1.1.1" /></xsl:when>
+        <xsl:when test="self::appendix"><xsl:number count="appendix" level="multiple" format="A.1.1.1.1.1.1.1" /></xsl:when>
+        <xsl:otherwise><xsl:number count="section" level="multiple"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- get the section number for the references section -->
+<xsl:template name="get-references-section-number">
+  <xsl:value-of select="count(/rfc/middle/section[not(@numbered) or @numbered!='false']) + count(/rfc/middle/ed:replace/ed:ins/section[not(@numbered) or @numbered!='false']) + 1"/>
+</xsl:template>
+
+<xsl:template name="emit-section-number">
+  <xsl:param name="no"/>
+  <xsl:param name="appendixPrefix" select="false()"/>
+  <xsl:if test="$appendixPrefix and translate($no,$ucase,'')=''">Appendix </xsl:if>
+  <xsl:value-of select="$no"/><xsl:if test="not(contains($no,'.')) or $xml2rfc-ext-sec-no-trailing-dots!='no'">.</xsl:if>
+</xsl:template>
+
+<xsl:template name="get-section-type">
+  <xsl:choose>
+    <xsl:when test="self::abstract">Abstract</xsl:when>
+    <xsl:when test="self::note">Note</xsl:when>
+    <xsl:when test="ancestor::back and not(self::references)">Appendix</xsl:when>
+    <xsl:otherwise>Section</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-table-number">
+  <xsl:choose>
+    <xsl:when test="self::table or @anchor!=''">
+      <xsl:number level="any" count="texttable[@anchor!='']|table" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>u.</xsl:text>
+      <xsl:number level="any" count="texttable[not(@anchor) or @anchor='']" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-table-anchor">
+  <xsl:value-of select="$anchor-pref"/>
+  <xsl:text>table.</xsl:text>
+  <xsl:call-template name="get-table-number"/>
+</xsl:template>
+
+<xsl:template name="get-figure-number">
+  <xsl:choose>
+    <xsl:when test="@anchor!='' or @title or name">
+      <xsl:number level="any" count="figure[@anchor!='' or @title or name]" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>u.</xsl:text>
+      <xsl:number level="any" count="figure[(not(@anchor) or @anchor='') and not(@title) and not(name)]" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-figure-anchor">
+  <xsl:value-of select="$anchor-pref"/>
+  <xsl:text>figure.</xsl:text>
+  <xsl:call-template name="get-figure-number"/>
+</xsl:template>
+
+<!-- reformat contents of author/@initials -->
+<xsl:template name="format-initials">
+  <xsl:param name="initials" select="@initials"/>
+
+  <xsl:variable name="computed-initials">
+    <xsl:choose>
+      <xsl:when test="normalize-space($initials)!=''">
+        <xsl:value-of select="$initials"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="get-author-initials"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+ 
+  <xsl:variable name="normalized" select="normalize-space($computed-initials)"/>
+
+  <xsl:choose>
+    <xsl:when test="$normalized=''">
+      <!-- nothing to do -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="r">
+        <xsl:call-template name="t-format-initials">
+          <xsl:with-param name="remainder" select="$normalized"/>
+        </xsl:call-template>
+      </xsl:variable>
+    
+      <xsl:if test="$r!=@initials">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">@initials '<xsl:value-of select="@initials"/>': did you mean '<xsl:value-of select="$r"/>'?</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    
+      <xsl:value-of select="$r"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="t-format-initials">
+  <xsl:param name="have"/>
+  <xsl:param name="remainder"/>
+
+  <xsl:variable name="first" select="substring($remainder,1,1)"/>
+  <xsl:variable name="prev" select="substring($have,string-length($have))"/>
+
+<!--<xsl:message>
+have: <xsl:value-of select="$have"/>
+remainder: <xsl:value-of select="$remainder"/>
+first: <xsl:value-of select="$first"/>
+prev: <xsl:value-of select="$prev"/>
+</xsl:message>-->
+
+  <xsl:choose>
+    <xsl:when test="$remainder='' and $prev!='.'">
+      <xsl:value-of select="concat($have,'.')"/>
+    </xsl:when>
+    <xsl:when test="$remainder=''">
+      <xsl:value-of select="$have"/>
+    </xsl:when>
+    <xsl:when test="$prev='.' and $first='.'">
+      <!-- repeating dots -->
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="$have"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <!-- missing dot before '-' -->
+<!--    <xsl:when test="$prev!='.' and $first='-'">
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="concat($have,'.-')"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:when>-->
+    <!-- missing space after '.' -->
+<!--    <xsl:when test="$prev='.' and $first!=' '">
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="concat($have,' ',$first)"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:when>-->
+    <xsl:otherwise>
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="concat($have,$first)"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="truncate-initials">
+  <xsl:param name="initials"/>
+  <xsl:variable name="local-multiple-initials">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="../../processing-instruction('rfc')|../processing-instruction('rfc')|./processing-instruction('rfc')"/>
+      <xsl:with-param name="attr" select="'multiple-initials'"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:variable name="use-multiple-initials">
+    <xsl:choose>
+      <xsl:when test="$local-multiple-initials!=''">
+        <xsl:value-of select="$local-multiple-initials"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$xml2rfc-multiple-initials"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="normalize-space($initials)=''"/>
+    <xsl:when test="$use-multiple-initials='yes'">
+      <xsl:value-of select="$initials"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat(substring-before($initials,'.'),'.')"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- see https://chromium-i18n.appspot.com/ssl-address -->
+<countries xmlns="mailto:julian.reschke@greenbytes.de?subject=rfc2629.xslt">
+  <c c2="AR" c3="ARG" sn="Argentina" fmt="%A%n%Z %C%n%S"/>
+  <c c2="AU" c3="AUS" sn="Australia" fmt="%A%n%C %S %Z"/>
+  <c c2="AT" c3="AUT" sn="Austria" fmt="%A%n%Z %C"/>
+  <c c2="BE" c3="BEL" sn="Belgium" fmt="%A%n%Z %C"/>
+  <c c2="BR" c3="BRA" sn="Brazil" fmt="%A%n%D%n%C-%S%n%Z"/>
+  <c c2="CA" c3="CAN" sn="Canada" fmt="%A%n%C %S %Z"/>
+  <c c2="CL" c3="CHL" sn="Chile" fmt="%A%n%Z %C%n%S"/>
+  <c c2="CN" c3="CHN" sn="China" fmt="%Z%n%S%C%D%n%A"/>
+  <c c2="HR" c3="HRV" sn="Croatia" fmt="%A%n%Z %C" postprefix="HR-"/>
+  <c c2="CZ" c3="CZE" sn="Czechia" fmt="%A%n%Z %C"/>
+  <c c2="DK" c3="DNK" sn="Denmark" fmt="%A%n%Z %C"/>
+  <c c2="DE" c3="DEU" sn="Germany" fmt="%A%n%Z %C"/>
+  <c c2="GR" c3="GRC" sn="Greece" fmt="%A%n%Z %C"/>
+  <c c2="FI" c3="FIN" sn="Finland" fmt="%A%n%Z %C" postprefix="FI-"/>/>
+  <c c2="FR" c3="FRA" sn="France" fmt="%A%n%Z %C"/>
+  <c c2="HU" c3="HUN" sn="Hungary" fmt="%C%n%A%n%Z"/>
+  <c c2="IN" c3="IND" sn="India" fmt="%A%n%C %Z%n%S"/>
+  <c c2="IR" c3="IRL" sn="Ireland" fmt="%A%n%D%n%C%n%S %Z"/>
+  <c c2="IL" c3="ISR" sn="Israel" fmt="%A%n%C %Z"/>
+  <c c2="IT" c3="ITA" sn="Italy" fmt="%A%n%Z %C %S"/>
+  <c c2="JP" c3="JPN" sn="Japan" fmt="%Z%n%S%n%A" postprefix="&#12306;"/>
+  <c c2="KR" c3="KOR" sn="Korea (the Republic of)" fmt="%A%n%C, %S %Z"/>
+  <c c2="LU" c3="LUX" sn="Luxembourg" fmt="%A%n%Z %C" postprefix="L-"/>
+  <c c2="MU" c3="MUS" sn="Mauritius" fmt="%A%n%Z%n%C"/>
+  <c c2="MX" c3="MEX" sn="Mexico" fmt="%A%n%D%n%Z %C, %S"/>
+  <c c2="NL" c3="NLD" sn="Netherlands" fmt="%A%n%Z %C"/>
+  <c c2="NZ" c3="NZL" sn="New Zealand" fmt="%A%n%D%n%C %Z"/>
+  <c c2="NO" c3="NOR" sn="Norway" fmt="%A%n%Z %C"/>
+  <c c2="PL" c3="POL" sn="Poland" fmt="%A%n%Z %C"/>
+  <c c2="PT" c3="PRT" sn="Portugal" fmt="%A%n%Z %C"/>
+  <c c2="RO" c3="ROU" sn="Romania" fmt="%A%n%Z %C"/>
+  <c c2="RU" c3="RUS" sn="Russian Federation" fmt="%A%n%C%n%S%n%Z"/>
+  <c c2="SG" c3="SGP" sn="Singapore" fmt="%A%n%Z" postprefix="SINGAPORE "/>
+  <c c2="SK" c3="SVK" sn="Slovakia" fmt="%A%n%Z %C"/>
+  <c c2="SI" c3="SVN" sn="Slovenia" fmt="%A%n%Z %C" postprefix="SI-"/>
+  <c c2="ES" c3="ESP" sn="Spain" fmt="%A%n%Z %C %S"/>
+  <c c2="SE" c3="SWE" sn="Sweden" fmt="%A%n%Z %C" postprefix="SE-"/>
+  <c c2="CH" c3="CHE" sn="Switzerland" fmt="%A%n%Z %C" postprefix="CH-"/>
+  <c c2="TH" c3="THA" sn="Thailand" fmt="%A%n%D %C%n%S %Z"/>
+  <c c2="TR" c3="TUR" sn="Turkey" fmt="%A%n%Z %C/%S"/>
+  <c c2="GB" c3="GBR" sn="United Kingdom of Great Britain and Northern Ireland" alias1="UK" fmt="%A%n%C%n%Z"/>
+  <c c2="US" c3="USA" sn="United States of America" fmt="%A%n%C, %S %Z"/>
+  <c c2="UY" c3="URY" sn="Uruguay" fmt="%A%n%Z %C %S"/>
+</countries>
+
+<xsl:template name="get-country-format">
+  <xsl:param name="country"/>
+  <xsl:variable name="countries" select="document('')/*/myns:countries/myns:c"/>
+
+  <xsl:variable name="short" select="translate(normalize-space(translate($country,'.','')),$lcase,$ucase)"/>
+
+  <xsl:choose>
+    <xsl:when test="$countries[@sn=$country]">
+      <!-- all good -->
+      <xsl:value-of select="$countries[@sn=$country]/@fmt"/>
+    </xsl:when>
+    <xsl:when test="$short=''">
+      <!-- already warned -->
+    </xsl:when>
+    <xsl:when test="not($countries/@sn=$country) and ($countries/@c3=$short)">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">'<xsl:value-of select="$country"/>' is not an ISO country short name, maybe you meant '<xsl:value-of select="$countries[@c3=$short]/@sn"/>'?</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not($countries/@sn=$country) and ($countries/@c2=$short)">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">'<xsl:value-of select="$country"/>' is not an ISO country short name, maybe you meant '<xsl:value-of select="$countries[@c2=$short]/@sn"/>'?</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not($countries/@sn=$country) and ($countries/@alias1=$short)">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">'<xsl:value-of select="$country"/>' is not an ISO country short name, maybe you meant '<xsl:value-of select="$countries[@alias1=$short]/@sn"/>'?</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="$countries[starts-with(translate(@sn,$lcase,$ucase),$short)]">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">'<xsl:value-of select="$country"/>' is not an ISO country short name, maybe you meant '<xsl:value-of select="$countries[starts-with(translate(@sn,$lcase,$ucase),$short)][1]/@sn"/>'? (lookup of short names: https://www.iso.org/obp/ui/)</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">ISO country short name '<xsl:value-of select="$country"/>' unknown (lookup of short names: https://www.iso.org/obp/ui/)</xsl:with-param>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-country-postprefix">
+  <xsl:param name="country"/>
+  <xsl:variable name="countries" select="document('')/*/myns:countries/myns:c"/>
+  <xsl:value-of select="$countries[@sn=$country]/@postprefix"/>
+</xsl:template>
+
+<xsl:template name="extract-normalized">
+  <xsl:param name="node" select="."/>
+  <xsl:param name="ascii" select="false()"/>
+
+  <xsl:variable name="name" select="local-name($node)"/>
+
+  <xsl:variable name="n">
+    <xsl:choose>
+      <xsl:when test="$ascii and $node/@ascii!=''">
+        <xsl:value-of select="$node/@ascii"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$node"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <xsl:variable name="text" select="normalize-space($n)"/>
+  <xsl:if test="string-length($n) != string-length($text)">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">excessive whitespace in <xsl:value-of select="$name"/>: '<xsl:value-of select="$n"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$text=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">missing text in <xsl:value-of select="$name"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  
+  <xsl:value-of select="$text"/>
+</xsl:template>
+
+<!-- checking for email element -->
+<xsl:template name="extract-email">
+  <xsl:variable name="email" select="normalize-space(.)"/>
+  <xsl:if test="contains($email,' ')">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg">whitespace in email address: '<xsl:value-of select="."/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="email2">
+    <xsl:choose>
+      <xsl:when test="starts-with($email,'mailto:')">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">email should not include URI scheme: '<xsl:value-of select="."/>'</xsl:with-param>
+        </xsl:call-template>
+        <xsl:value-of select="substring($email, 1 + string-length('mailto:'))"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$email"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="$email2"/>
+</xsl:template>
+
+<!-- checking for uri element -->
+<xsl:template name="extract-uri">
+  <xsl:variable name="uri" select="normalize-space(.)"/>
+  <xsl:if test="string-length(.) != string-length($uri) or contains($uri,' ')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">excessive whitespace in URI: '<xsl:value-of select="."/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="$uri=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">URI is empty</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:value-of select="$uri"/>
+</xsl:template>
+
+<xsl:template name="insert-conditional-pagebreak">
+  <xsl:if test="$xml2rfc-compact!='yes'">
+    <xsl:attribute name="class">np</xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insert-conditional-hrule">
+  <xsl:if test="$xml2rfc-compact!='yes'">
+    <hr class="{$css-noprint}" />
+  </xsl:if>
+</xsl:template>
+
+<!-- get text content from marked-up text -->
+
+<xsl:template match="text()" mode="get-text-content">
+  <xsl:value-of select="normalize-space(.)"/>
+</xsl:template>
+
+<xsl:template match="br" mode="get-text-content">
+  <xsl:text> </xsl:text>
+</xsl:template>
+
+<xsl:template match="*" mode="get-text-content">
+  <xsl:apply-templates mode="get-text-content"/>
+</xsl:template>
+
+<xsl:template match="ed:del" mode="get-text-content">
+</xsl:template>
+
+<!-- parsing of processing instructions -->
+<xsl:template name="parse-pis">
+  <xsl:param name="nodes"/>
+  <xsl:param name="attr"/>
+  <xsl:param name="sep"/>
+  <xsl:param name="ret"/>
+  <xsl:param name="default"/>
+  <xsl:param name="duplicate-warning" select="'yes'"/>
+
+  <xsl:choose>
+    <xsl:when test="count($nodes)=0">
+      <xsl:choose>
+        <xsl:when test="$ret!=''">
+          <xsl:value-of select="$ret"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$default"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="ret2">
+        <xsl:for-each select="$nodes[1]">
+          <xsl:call-template name="parse-one-pi">
+            <xsl:with-param name="str" select="."/>
+            <xsl:with-param name="attr" select="$attr"/>
+            <xsl:with-param name="sep" select="$sep"/>
+            <xsl:with-param name="ret" select="$ret"/>
+            <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:variable>
+
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$nodes[position()!=1]"/>
+        <xsl:with-param name="attr" select="$attr"/>
+        <xsl:with-param name="sep" select="$sep"/>
+        <xsl:with-param name="ret" select="$ret2"/>
+        <xsl:with-param name="default" select="$default"/>
+        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+      </xsl:call-template>
+
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="parse-one-pi">
+  <xsl:param name="str"/>
+  <xsl:param name="attr"/>
+  <xsl:param name="sep"/>
+  <xsl:param name="ret"/>
+  <xsl:param name="duplicate-warning"/>
+
+  <xsl:variable name="str2">
+    <xsl:call-template name="eat-leading-whitespace">
+      <xsl:with-param name="str" select="$str"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$str2=''">
+      <!-- done -->
+      <xsl:value-of select="$ret"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="attrname" select="substring-before($str2,'=')"/>
+
+      <xsl:choose>
+        <xsl:when test="$attrname=''">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">bad PI syntax: <xsl:value-of select="$str2"/></xsl:with-param>
+          </xsl:call-template>
+          <xsl:value-of select="$ret"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:variable name="remainder" select="substring($str2,2+string-length($attrname))"/>
+          <xsl:choose>
+            <xsl:when test="string-length($remainder) &lt; 2">
+              <xsl:call-template name="warning">
+                <xsl:with-param name="msg">bad PI value syntax: <xsl:value-of select="$remainder"/></xsl:with-param>
+              </xsl:call-template>
+              <xsl:value-of select="$ret"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:variable name="rem">
+                <xsl:call-template name="eat-leading-whitespace">
+                  <xsl:with-param name="str" select="$remainder"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <xsl:variable name="qchars">&apos;&quot;</xsl:variable>
+              <xsl:variable name="qchar" select="substring($rem,1,1)"/>
+              <xsl:variable name="rem2" select="substring($rem,2)"/>
+              <xsl:choose>
+                <xsl:when test="not(contains($qchars,$qchar))">
+                  <xsl:call-template name="warning">
+                    <xsl:with-param name="msg">pseudo-attribute value needs to be quoted: <xsl:value-of select="$rem"/></xsl:with-param>
+                  </xsl:call-template>
+                  <xsl:value-of select="$ret"/>
+                </xsl:when>
+                <xsl:when test="not(contains($rem2,$qchar))">
+                  <xsl:call-template name="warning">
+                    <xsl:with-param name="msg">unmatched quote in: <xsl:value-of select="$rem2"/></xsl:with-param>
+                  </xsl:call-template>
+                  <xsl:value-of select="$ret"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:variable name="value" select="substring-before($rem2,$qchar)"/>
+
+                  <!-- check pseudo-attribute names -->
+                  <xsl:if test="name()='rfc-ext' and $attr='SANITYCHECK'">
+                    <xsl:choose>
+                      <xsl:when test="$attrname='abort-on'"/>
+                      <xsl:when test="$attrname='allow-markup-in-artwork'"/>
+                      <xsl:when test="$attrname='authors-section'"/>
+                      <xsl:when test="$attrname='check-artwork-width'"/>
+                      <xsl:when test="$attrname='css-contents'"/>
+                      <xsl:when test="$attrname='css-resource'"/>
+                      <xsl:when test="$attrname='duplex'"/>
+                      <xsl:when test="$attrname='html-pretty-print'"/>
+                      <xsl:when test="$attrname='include-index'"/>
+                      <xsl:when test="$attrname='include-references-in-index'"/>
+                      <xsl:when test="$attrname='internet-draft-uri'"/>
+                      <xsl:when test="$attrname='justification'"/>
+                      <xsl:when test="$attrname='log-level'"/>
+                      <xsl:when test="$attrname='paragraph-links'"/>
+                      <xsl:when test="$attrname='parse-xml-in-artwork'"/>
+                      <xsl:when test="$attrname='refresh-from'"/>
+                      <xsl:when test="$attrname='refresh-interval'"/>
+                      <xsl:when test="$attrname='refresh-xslt'"/>
+                      <xsl:when test="$attrname='rfc-uri'"/>
+                      <xsl:when test="$attrname='sec-no-trailing-dots'"/>
+                      <xsl:when test="$attrname='trace-parse-xml'"/>
+                      <xsl:when test="$attrname='ucd-file'"/>
+                      <xsl:when test="$attrname='use-system-time'"/>
+                      <xsl:when test="$attrname='vspace-pagebreak'"/>
+                      <xsl:when test="$attrname='xml2rfc-backend'"/>
+                      <xsl:when test="$attrname='xref-with-text-generate'"/>
+                      <xsl:otherwise>
+                        <xsl:call-template name="warning">
+                          <xsl:with-param name="msg">unsupported rfc-ext pseudo-attribute '<xsl:value-of select="$attrname"/>'</xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:otherwise>
+                    </xsl:choose>
+                  </xsl:if>
+
+                  <xsl:if test="name()='rfc' and $attr='SANITYCHECK'">
+                    <xsl:choose>
+                      <xsl:when test="$attrname='authorship'"/>
+                      <xsl:when test="$attrname='comments'"/>
+                      <xsl:when test="$attrname='compact'"/>
+                      <xsl:when test="$attrname='docmapping'">
+                        <xsl:if test="$value!='yes'">
+                          <xsl:call-template name="warning">
+                            <xsl:with-param name="msg">the rfc docmapping pseudo-attribute with values other than 'yes' in not supported by this processor.</xsl:with-param>
+                          </xsl:call-template>
+                        </xsl:if>
+                      </xsl:when>
+                      <xsl:when test="$attrname='editing'"/>
+                      <xsl:when test="$attrname='footer'"/>
+                      <xsl:when test="$attrname='header'"/>
+                      <xsl:when test="$attrname='include'">
+                        <xsl:choose>
+                          <xsl:when test="not(parent::references)">
+                            <xsl:call-template name="error">
+                              <xsl:with-param name="msg">the rfc include pseudo-attribute (unless a child node of &lt;references&gt;) is not supported by this processor, see http://greenbytes.de/tech/webdav/rfc2629xslt/rfc2629xslt.html#examples.internalsubset for alternative syntax.</xsl:with-param>
+                            </xsl:call-template>
+                          </xsl:when>
+                          <xsl:otherwise>
+                            <xsl:call-template name="warning">
+                              <xsl:with-param name="msg">the rfc include pseudo-attribute is only partially supported by this processor, see http://greenbytes.de/tech/webdav/rfc2629xslt/rfc2629xslt.html#examples.internalsubset for alternative syntax.</xsl:with-param>
+                            </xsl:call-template>
+                          </xsl:otherwise>
+                        </xsl:choose>
+                      </xsl:when>
+                      <xsl:when test="$attrname='inline'"/>
+                      <xsl:when test="$attrname='iprnotified'"/>
+                      <xsl:when test="$attrname='linefile'"/>
+                      <xsl:when test="$attrname='linkmailto'"/>
+                      <xsl:when test="$attrname='multiple-initials'"/>
+                      <xsl:when test="$attrname='private'"/>
+                      <xsl:when test="$attrname='rfcedstyle'"/>
+                      <xsl:when test="$attrname='sortrefs'"/>
+                      <xsl:when test="$attrname='subcompact'"/>
+                      <xsl:when test="$attrname='strict'"/>
+                      <xsl:when test="$attrname='symrefs'"/>
+                      <xsl:when test="$attrname='toc'"/>
+                      <xsl:when test="$attrname='tocdepth'"/>
+                      <xsl:when test="$attrname='tocindent'">
+                        <xsl:if test="$value!='yes'">
+                          <xsl:call-template name="warning">
+                            <xsl:with-param name="msg">the rfc tocindent pseudo-attribute with values other than 'yes' in not supported by this processor.</xsl:with-param>
+                          </xsl:call-template>
+                        </xsl:if>
+                      </xsl:when>
+                      <xsl:otherwise>
+                        <xsl:call-template name="info">
+                          <xsl:with-param name="msg">unsupported rfc pseudo-attribute '<xsl:value-of select="$attrname"/>'</xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:otherwise>
+                    </xsl:choose>
+                  </xsl:if>
+
+                  <xsl:choose>
+                    <xsl:when test="$attrname != $attr">
+                      <!-- pseudo-attr does not match, continue -->
+                      <xsl:call-template name="parse-one-pi">
+                        <xsl:with-param name="str" select="substring($rem2, 2 + string-length($value))"/>
+                        <xsl:with-param name="attr" select="$attr"/>
+                        <xsl:with-param name="sep" select="$sep"/>
+                        <xsl:with-param name="ret" select="$ret"/>
+                        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+                      </xsl:call-template>
+                    </xsl:when>
+                    <xsl:when test="$sep='' and $ret!=''">
+                      <!-- pseudo-attr does match, but we only want one value -->
+                      <xsl:if test="$ret != $value and $duplicate-warning='yes'">
+                        <xsl:call-template name="warning">
+                          <xsl:with-param name="msg">duplicate pseudo-attribute <xsl:value-of select="$attr"/>, overwriting value <xsl:value-of select="$ret"/></xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:if>
+                      <xsl:call-template name="parse-one-pi">
+                        <xsl:with-param name="str" select="substring($rem2, 2 + string-length($value))"/>
+                        <xsl:with-param name="attr" select="$attr"/>
+                        <xsl:with-param name="sep" select="$sep"/>
+                        <xsl:with-param name="ret" select="$value"/>
+                        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+                      </xsl:call-template>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      <!-- pseudo-attr does match -->
+                      <xsl:call-template name="parse-one-pi">
+                        <xsl:with-param name="str" select="substring($rem2, 2 + string-length($value))"/>
+                        <xsl:with-param name="attr" select="$attr"/>
+                        <xsl:with-param name="sep" select="$sep"/>
+                        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+                        <xsl:with-param name="ret">
+                          <xsl:choose>
+                            <xsl:when test="$ret!=''">
+                              <xsl:value-of select="concat($ret,$sep,$value)"/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                              <xsl:value-of select="$value"/>
+                            </xsl:otherwise>
+                          </xsl:choose>
+                        </xsl:with-param>
+                      </xsl:call-template>
+                    </xsl:otherwise>
+
+                  </xsl:choose>
+
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="eat-leading-whitespace">
+  <xsl:param name="str"/>
+
+  <xsl:choose>
+    <xsl:when test="$str=''">
+    </xsl:when>
+    <xsl:when test="translate(substring($str,1,1),' &#10;&#13;&#9;',' ')=' '">
+      <xsl:call-template name="eat-leading-whitespace">
+        <xsl:with-param name="str" select="substring($str,2)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$str"/>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<!-- diag support -->
+<xsl:template name="lineno">
+  <xsl:if test="function-available('saxon-old:line-number')" use-when="function-available('saxon-old:line-number')">
+    <xsl:if test="saxon-old:line-number() > 0">
+      <xsl:text> (at line </xsl:text>
+      <xsl:value-of select="saxon-old:line-number()"/>
+      <xsl:if test="function-available('saxon-old:systemId')">
+        <xsl:variable name="rootsys">
+          <xsl:for-each select="/*">
+            <xsl:value-of select="saxon-old:systemId()"/>
+          </xsl:for-each>
+        </xsl:variable>
+        <xsl:if test="$rootsys != saxon-old:systemId()">
+          <xsl:text> of </xsl:text>
+          <xsl:value-of select="saxon-old:systemId()"/>
+        </xsl:if>
+      </xsl:if>
+      <xsl:text>)</xsl:text>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="function-available('saxon:line-number')" use-when="function-available('saxon:line-number')">
+    <xsl:if test="saxon:line-number() > 0">
+      <xsl:text> (at line </xsl:text>
+      <xsl:value-of select="saxon:line-number()"/>
+      <xsl:if test="function-available('saxon:systemId')">
+        <xsl:variable name="rootsys">
+          <xsl:for-each select="/*">
+            <xsl:value-of select="saxon:systemId()"/>
+          </xsl:for-each>
+        </xsl:variable>
+        <xsl:if test="$rootsys != saxon:systemId()">
+          <xsl:text> of </xsl:text>
+          <xsl:value-of select="saxon:systemId()"/>
+        </xsl:if>
+      </xsl:if>
+      <xsl:text>)</xsl:text>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+<!-- define exslt:node-set for msxml -->
+<msxsl:script language="JScript" implements-prefix="exslt">
+  this['node-set'] = function (x) {
+    return x;
+  }
+</msxsl:script>
+
+<!-- date handling -->
+
+<msxsl:script language="JScript" implements-prefix="date">
+  function twodigits(s) {
+    return s &lt; 10 ? "0" + s : s;
+  }
+
+  this['date-time'] = function (x) {
+    var now = new Date();
+    var offs = now.getTimezoneOffset();
+    return now.getFullYear() + "-"
+      + twodigits(1 + now.getMonth()) + "-"
+      + twodigits(now.getDate()) + "T"
+      + twodigits(now.getHours()) + ":"
+      + twodigits(now.getMinutes()) + ":"
+      + twodigits(now.getSeconds())
+      + (offs >= 0 ? "-" : "+")
+      + twodigits(Math.abs(offs) / 60) + ":"
+      + twodigits(Math.abs(offs) % 60);
+  }
+</msxsl:script>
+
+<xsl:variable name="current-year">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-use-system-time='no'"/>
+    <xsl:when test="function-available('date:date-time')" use-when="function-available('date:date-time')">
+      <xsl:value-of select="substring-before(date:date-time(),'-')"/>
+    </xsl:when>
+    <xsl:when test="function-available('current-date')">
+      <xsl:value-of select="substring-before(string(current-date()),'-')"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="current-month">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-use-system-time='no'"/>
+    <xsl:when test="function-available('date:date-time')" use-when="function-available('date:date-time')">
+      <xsl:value-of select="substring-before(substring-after(date:date-time(),'-'),'-')"/>
+    </xsl:when>
+    <xsl:when test="function-available('current-date')">
+      <xsl:value-of select="substring-before(substring-after(string(current-date()),'-'),'-')"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="current-day">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-use-system-time='no'"/>
+    <xsl:when test="function-available('date:date-time')" use-when="function-available('date:date-time')">
+      <xsl:value-of select="substring-after(substring-after(substring-before(date:date-time(),'T'),'-'),'-')"/>
+    </xsl:when>
+    <xsl:when test="function-available('current-dateTime')">
+      <xsl:value-of select="substring-after(substring-after(substring-before(string(current-dateTime()),'T'),'-'),'-')"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="may-default-dates">
+  <xsl:choose>
+    <xsl:when test="$current-year!='' and $current-month!='' and $current-day!=''">
+      <xsl:variable name="year-specified" select="/rfc/front/date/@year and /rfc/front/date/@year!=''"/>
+      <xsl:variable name="month-specified" select="/rfc/front/date/@month and /rfc/front/date/@month!=''"/>
+      <xsl:variable name="day-specified" select="/rfc/front/date/@day and /rfc/front/date/@day!=''"/>
+      <xsl:variable name="system-month">
+        <xsl:call-template name="get-month-as-name">
+          <xsl:with-param name="month" select="$current-month"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="$year-specified and /rfc/front/date/@year!=$current-year">Specified year <xsl:value-of select="/rfc/front/date/@year"/> does not match system date (<xsl:value-of select="$current-year"/>)</xsl:when>
+        <xsl:when test="$month-specified and /rfc/front/date/@month!=$system-month">Specified month <xsl:value-of select="/rfc/front/date/@month"/> does not match system date (<xsl:value-of select="$system-month"/>)</xsl:when>
+        <xsl:when test="$day-specified and /rfc/front/date/@day!=$current-day">Specified day does not match system date</xsl:when>
+        <xsl:when test="not($year-specified) and ($month-specified or $day-specified)">Can't default year when month or day is specified</xsl:when>
+        <xsl:when test="not($month-specified) and $day-specified">Can't default month when day is specified</xsl:when>
+        <xsl:otherwise>yes</xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <!-- may, but won't -->
+    <xsl:otherwise>yes</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:param name="xml2rfc-ext-pub-year">
+  <xsl:choose>
+    <xsl:when test="/rfc/front/date/@year and /rfc/front/date/@year!=''">
+      <xsl:value-of select="/rfc/front/date/@year"/>
+    </xsl:when>
+    <xsl:when test="$current-year!='' and $may-default-dates='yes'">
+      <xsl:value-of select="$current-year"/>
+    </xsl:when>
+    <xsl:when test="$current-year!='' and $may-default-dates!='yes'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="$may-default-dates"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'/rfc/front/date/@year missing (and XSLT processor cannot compute the system date)'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-pub-month">
+  <xsl:choose>
+    <xsl:when test="/rfc/front/date/@month and /rfc/front/date/@month!=''">
+      <xsl:variable name="m" select="/rfc/front/date/@month"/>
+      <xsl:choose>
+        <xsl:when test="string(number($m))!='NaN' and number($m) &gt; 0 and number($m) &lt; 13">
+          <xsl:call-template name="get-month-as-name">
+            <xsl:with-param name="month" select="$m"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$m"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="$current-month!='' and $may-default-dates='yes'">
+      <xsl:call-template name="get-month-as-name">
+        <xsl:with-param name="month" select="$current-month"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="$current-month!='' and $may-default-dates!='yes'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="$may-default-dates"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'/rfc/front/date/@month missing (and XSLT processor cannot compute the system date)'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="pub-month-numeric">
+  <xsl:call-template name="get-month-as-num">
+    <xsl:with-param name="month" select="$xml2rfc-ext-pub-month" />
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-pub-day">
+  <xsl:choose>
+    <xsl:when test="/rfc/front/date/@day and /rfc/front/date/@day!=''">
+      <xsl:value-of select="/rfc/front/date/@day"/>
+    </xsl:when>
+    <xsl:when test="$current-day!='' and $may-default-dates='yes'">
+      <xsl:value-of select="$current-day"/>
+    </xsl:when>
+    <xsl:otherwise /> <!-- harmless, we just don't have it -->
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="pub-yearmonth">
+  <!-- year or 0000 -->
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-pub-year!=''">
+      <xsl:value-of select="format-number($xml2rfc-ext-pub-year,'0000')"/>
+    </xsl:when>
+    <xsl:otherwise>0000</xsl:otherwise>
+  </xsl:choose>
+  <!-- month or 00 -->
+  <xsl:choose>
+    <xsl:when test="number($pub-month-numeric) &gt; 0">
+      <xsl:value-of select="format-number($pub-month-numeric,'00')"/>
+    </xsl:when>
+    <xsl:otherwise>00</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- <u> element -->
+<xsl:template name="convert-u-compact-remainder">
+  <xsl:param name="f"/>
+  <xsl:choose>
+    <xsl:when test="contains($f,'-')">
+      <xsl:text>{</xsl:text>
+        <xsl:value-of select="substring-before($f,'-')"/>
+      <xsl:text>}, </xsl:text>
+      <xsl:call-template name="convert-u-compact-remainder">
+        <xsl:with-param name="f" select="substring-after($f,'-')"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>{</xsl:text>
+      <xsl:value-of select="$f"/>
+      <xsl:text>}</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="convert-u-compact">
+  <xsl:param name="f"/>
+  <xsl:choose>
+    <xsl:when test="contains($f,'-')">
+      <xsl:text>{</xsl:text>
+      <xsl:value-of select="substring-before($f,'-')"/>
+      <xsl:text>}</xsl:text>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>{</xsl:text>
+      <xsl:value-of select="$f"/>
+      <xsl:text>}</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:if test="contains($f,'-')">
+    <xsl:text> (</xsl:text>
+      <xsl:call-template name="convert-u-compact-remainder">
+        <xsl:with-param name="f" select="substring-after($f,'-')"/>
+      </xsl:call-template>
+    <xsl:text>)</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="u-hex2">
+  <xsl:param name="n"/>
+  <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
+
+  <xsl:if test="$n &gt;= 16">
+    <xsl:call-template name="u-hex2">
+      <xsl:with-param name="n" select="floor($n div 16)"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:value-of select="substring($hex, 1 + ($n mod 16), 1)"/>
+</xsl:template>
+
+<xsl:template name="u-hex">
+  <xsl:param name="n"/>
+  <xsl:variable name="t">
+    <xsl:call-template name="u-hex2">
+      <xsl:with-param name="n" select="$n"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:if test="string-length($t) &lt; 4">
+    <xsl:value-of select="substring('0000',1,4-string-length($t))"/>
+  </xsl:if>
+  <xsl:value-of select="$t"/>
+</xsl:template>
+
+<xsl:template name="u-expanded-name">
+  <xsl:param name="lit"/>
+  <xsl:choose>
+    <xsl:when test="string-length($lit)=0"></xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="name-of-first-char">
+        <xsl:with-param name="lit" select="$lit"/>
+      </xsl:call-template>
+      <xsl:if test="string-length($lit)!=1">
+        <xsl:text>, </xsl:text>
+        <xsl:call-template name="u-expanded-name">
+          <xsl:with-param name="lit" select="substring($lit,2)"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:otherwise> 
+  </xsl:choose>  
+</xsl:template>
+
+<xsl:template name="name-of-first-char">
+  <xsl:param name="lit"/>
+  <xsl:variable name="c" select="substring($lit,1,1)"/>
+  <xsl:variable name="map" select="//x:u-map/x:c[@c=$c][@d]"/>
+  <xsl:choose>
+    <xsl:when test="$map">
+      <xsl:value-of select="$map/@d"/>
+    </xsl:when>
+    <xsl:when test="$xml2rfc-ext-ucd-file!='' and document($xml2rfc-ext-ucd-file)/x:u-map/x:c[@c=$c]">
+      <xsl:value-of select="document($xml2rfc-ext-ucd-file)/x:u-map/x:c[@c=$c]/@d"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>???</xsl:text>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">
+          <xsl:text>'name' expansion for &lt;u>: character '</xsl:text>
+          <xsl:value-of select="$c"/>
+          <xsl:text>' requires local definition using x:u-map or local UCD mapping file </xsl:text>
+          <xsl:choose>
+            <xsl:when test="$xml2rfc-ext-ucd-file=''">which can be specified using the 'ucd-file' directive</xsl:when>
+            <xsl:otherwise>'<xsl:value-of select="$xml2rfc-ext-ucd-file"/>'</xsl:otherwise>
+          </xsl:choose>
+        </xsl:with-param>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="u-expanded-num">
+  <xsl:param name="lit"/>
+  <xsl:choose>
+    <xsl:when test="string-length($lit)=0"></xsl:when>
+    <xsl:otherwise>
+      <xsl:text>U+</xsl:text>
+      <xsl:variable name="n">
+        <xsl:call-template name="codepoint-of-first-char">
+          <xsl:with-param name="lit" select="$lit"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:call-template name="u-hex">
+        <xsl:with-param name="n" select="$n"/>
+      </xsl:call-template>
+      <xsl:if test="string-length($lit)!=1">
+        <xsl:text> </xsl:text>
+        <xsl:call-template name="u-expanded-num">
+          <xsl:with-param name="lit" select="substring($lit,2)"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:otherwise> 
+  </xsl:choose>  
+</xsl:template>
+
+<xsl:template name="codepoint-of-first-char">
+  <xsl:param name="lit"/>
+  <xsl:variable name="c" select="substring($lit,1,1)"/>
+  <xsl:choose>
+    <xsl:when test="not(function-available('string-to-codepoints'))">
+      <xsl:variable name="map" select="//x:u-map/x:c[@c=$c]"/>
+      <xsl:variable name="asciistring"> !"#$%&amp;'()*+,-./<xsl:value-of select="$digits"/>:;&lt;=>?@<xsl:value-of select="$ucase"/>[\]^_`<xsl:value-of select="$lcase"/>{|}~&#127;</xsl:variable>
+      <xsl:choose>
+        <xsl:when test="contains($asciistring,$c)">
+          <xsl:value-of select="32 + string-length(substring-before($asciistring,$c))"/>
+        </xsl:when>
+        <xsl:when test="$map">
+          <xsl:value-of select="number($map/@n)"/>
+        </xsl:when>
+        <xsl:when test="$xml2rfc-ext-ucd-file!='' and document($xml2rfc-ext-ucd-file)/x:u-map/x:c[@c=substring($lit,1,1)]">
+          <xsl:value-of select="document($xml2rfc-ext-ucd-file)/x:u-map/x:c[@c=substring($lit,1,1)]/@n"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">'<xsl:value-of select="$xml2rfc-ext-ucd-file"/>'
+              <xsl:text>'num' expansion for &lt;u>: character '</xsl:text>
+              <xsl:value-of select="$c"/>
+              <xsl:text>' requires XSLT 2, local definition using x:u-map, or local UCD mapping file </xsl:text>
+              <xsl:choose>
+                <xsl:when test="$xml2rfc-ext-ucd-file=''">which can be specified using the 'ucd-file' directive</xsl:when>
+                <xsl:otherwise>'<xsl:value-of select="$xml2rfc-ext-ucd-file"/>'</xsl:otherwise>
+              </xsl:choose>
+            </xsl:with-param>
+          </xsl:call-template>
+          <xsl:value-of select="number(65533)"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="number(string-to-codepoints($c))"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="u-expanded">
+  <xsl:param name="format"/>
+  <xsl:param name="lit"/>
+  <xsl:param name="ascii"/>
+
+  <xsl:choose>
+    <xsl:when test="starts-with($format,'{') and contains($format,'}')">
+      <xsl:variable name="tok" select="substring(substring-before($format,'}'),2)"/>
+      <xsl:choose>
+        <xsl:when test="$tok='lit'">
+          <xsl:text>"</xsl:text>
+          <xsl:value-of select="$lit"/>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:when test="$tok='ascii'">
+          <xsl:text>"</xsl:text>
+          <xsl:value-of select="$ascii"/>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:when test="$tok='name'">
+          <xsl:call-template name="u-expanded-name">
+            <xsl:with-param name="lit" select="$lit"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:when test="$tok='num'">
+          <xsl:call-template name="u-expanded-num">
+            <xsl:with-param name="lit" select="$lit"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">unknown expansion for &lt;u>: <xsl:value-of select="$tok"/></xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:call-template name="u-expanded">
+        <xsl:with-param name="format" select="substring-after($format,'}')"/>
+        <xsl:with-param name="lit" select="$lit"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="starts-with($format,'{')">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Broken format string for &lt;u>: <xsl:value-of select="$format"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(contains($format,'{'))">
+      <xsl:value-of select="$format"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="s" select="substring-before($format,'{')"/>
+      <xsl:value-of select="$s"/>
+      <xsl:call-template name="u-expanded">
+        <xsl:with-param name="format" select="substring($format, 1+string-length($s))"/>
+        <xsl:with-param name="lit" select="$lit"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>  
+</xsl:template>
+
+<xsl:template name="emit-u">
+  <xsl:variable name="format">
+    <xsl:choose>
+      <xsl:when test="@format!=''">
+        <xsl:value-of select="@format"/>
+      </xsl:when>
+      <xsl:otherwise>lit-name-num</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:variable name="f">
+    <xsl:choose>
+      <xsl:when test="translate($format,concat($lcase,'-'),'')=''">
+        <!-- compact notation -->
+        <xsl:call-template name="convert-u-compact">
+          <xsl:with-param name="f" select="$format"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$format"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:call-template name="u-expanded">
+    <xsl:with-param name="format" select="$f"/>
+    <xsl:with-param name="lit" select="."/>
+    <xsl:with-param name="ascii" select="@ascii"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="u">
+  <xsl:call-template name="emit-u"/>
+</xsl:template>
+
+<xsl:template match="x:u-map"/>
+
+<!-- simple validation support -->
+
+<xsl:template match="*" mode="validate">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="@*" mode="validate"/>
+
+<xsl:template name="validation-error">
+  <xsl:param name="additionalDiagnostics"/>
+  <xsl:variable name="pname">
+    <xsl:if test="namespace-uri(..)!=''">
+      <xsl:value-of select="concat('{',namespace-uri(..),'}')"/>
+    </xsl:if>
+    <xsl:value-of select="local-name(..)"/>
+  </xsl:variable>
+  <xsl:variable name="cname">
+    <xsl:if test="namespace-uri(.)!=''">
+      <xsl:value-of select="concat('{',namespace-uri(.),'}')"/>
+    </xsl:if>
+    <xsl:value-of select="local-name(.)"/>
+  </xsl:variable>
+  <xsl:call-template name="error">
+    <xsl:with-param name="msg" select="concat($cname,' not allowed inside ',$pname,$additionalDiagnostics)"/>
+    <xsl:with-param name="inline" select="'no'"/>
+  </xsl:call-template>
+</xsl:template>
+
+<!-- artwork/sourcecode element -->
+<xsl:template match="blockquote/artwork | figure/artwork | figure/ed:replace/ed:*/artwork | section/artwork | li/artwork | dd/artwork | artset/artwork" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="blockquote/sourcecode | figure/sourcecode | figure/ed:replace/ed:*/sourcecode | section/sourcecode | li/sourcecode | dd/sourcecode | td/sourcecode" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="artwork|sourcecode" mode="validate">
+  <xsl:call-template name="validation-error"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- author element -->
+<xsl:template match="front/author" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="author" mode="validate">
+  <xsl:call-template name="validation-error"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- li element -->
+<xsl:template match="ol/li | ul/li" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="li" mode="validate">
+  <xsl:call-template name="validation-error"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- list element -->
+<xsl:template match="t/list | t/ed:replace/ed:*/list" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="list" mode="validate">
+  <xsl:call-template name="validation-error"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- dl element -->
+<xsl:template match="abstract/dl | aside/dl | blockquote/dl | dd/dl | li/dl | note/dl | section/dl | td/dl | th/dl" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="dl" mode="validate">
+  <xsl:call-template name="validation-error"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- t element -->
+<xsl:template match="abstract/t | abstract/ed:replace/ed:*/t |
+                     list/t | list/ed:replace/ed:*/t |
+                     note/t | note/ed:replace/ed:*/t |
+                     section/t | section/ed:replace/ed:*/t |
+                     blockquote/t |
+                     x:blockquote/t | x:blockquote/ed:replace/ed:*/t |
+                     x:note/t | x:note/ed:replace/ed:*/t |
+                     aside/t |
+                     td/t | th/t |
+                     x:lt/t | li/t | x:lt/ed:replace/ed:*/t | dd/t" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="t" mode="validate">
+  <xsl:call-template name="validation-error"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- xref element -->
+<xsl:template match="abstract//xref" mode="validate">
+  <xsl:call-template name="validation-error">
+    <xsl:with-param name="additionalDiagnostics"> (inside &lt;abstract>)</xsl:with-param>
+  </xsl:call-template>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<xsl:template name="check-no-text-content">
+  <xsl:for-each select="text()">
+    <xsl:if test="normalize-space(.)!=''">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">No text content allowed inside &lt;<xsl:value-of select="local-name(..)"/>&gt;, but found: '<xsl:value-of select="."/>'</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="render-name">
+  <xsl:param name="n"/>
+  <xsl:param name="strip-links" select="true()"/>
+  <xsl:variable name="t">
+    <xsl:apply-templates select="$n"/>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="not($strip-links)">
+      <xsl:copy-of select="exslt:node-set($t)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates select="exslt:node-set($t)" mode="strip-links"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="render-name-ref">
+  <xsl:param name="n"/>
+  <xsl:variable name="t">
+    <xsl:call-template name="render-name">
+      <xsl:with-param name="n" select="$n"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:apply-templates select="exslt:node-set($t)" mode="strip-ids-and-linebreaks"/>
+</xsl:template>
+
+<!-- clean up links from HTML -->
+<xsl:template match="comment()|@*" mode="strip-links"><xsl:copy/></xsl:template>
+<xsl:template match="text()" mode="strip-links"><xsl:copy/></xsl:template>
+<xsl:template match="*" mode="strip-links">
+  <xsl:element name="{local-name()}">
+  	<xsl:apply-templates select="@*|node()" mode="strip-links" />
+  </xsl:element>
+</xsl:template>
+<xsl:template match="a|xhtml:a" mode="strip-links" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+  <xsl:choose>
+    <xsl:when test="@id">
+      <span id="{@id}">
+      	<xsl:apply-templates select="node()" mode="strip-links" />
+      </span>
+    </xsl:when>
+    <xsl:otherwise>
+    	<xsl:apply-templates select="node()" mode="strip-links" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="node()|@*" mode="strip-ids-and-linebreaks">
+  <xsl:copy>
+	  <xsl:apply-templates select="node()|@*" mode="strip-ids-and-linebreaks" />
+  </xsl:copy>
+</xsl:template>
+<xsl:template match="xhtml:br" mode="strip-ids-and-linebreaks">
+  <xsl:text> </xsl:text>
+</xsl:template>
+<xsl:template match="@id" mode="strip-ids-and-linebreaks"/>
+
+
+<!-- customization: these templates can be overridden in an XSLT that imports from this one -->
+<xsl:template name="add-start-material"/>
+<xsl:template name="add-end-material"/>
+
+</xsl:transform>
diff --git a/test/fixtures/cache-tests/spec/lib/rfc2629-other.ent b/test/fixtures/cache-tests/spec/lib/rfc2629-other.ent
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/rfc2629-other.ent
@@ -0,0 +1,61 @@
+<!-- rfc2629-other.ent
+
+     Character entity set for RFC-2629 source xml documents.
+     There is no need to invoke this directly from
+     the source document itself; just invoke the DTD.
+
+     These are unknown to (X)HTML, so any converter to these
+     formats must either substitute these entities
+     or invoke this file from the output file.
+
+     Conversion to txt or nroff format will replace
+     these entities by the parenthesized text.
+
+     Typical invocation:
+       <!ENTITY % rfc2629-other
+                PUBLIC "-//IETF//ENTITIES Other for RFC 2629//EN"
+                       "http://xml.resource.org/authoring/rfc2629-other.ent">
+       %rfc2629-other;
+-->
+
+<!-- Magical -->
+<!--     rfc.number            (automatically expanded to content
+                                of number="..." attribute
+                                of <rfc> element, or to "XXXX")                       -->
+
+<!-- ASCII -->
+<!ENTITY excl     "&#33;"><!-- U+0021 EXCLAMATION MARK                  ("!")         -->
+<!ENTITY num      "&#35;"><!-- U+0023 NUMBER SIGN                       ("#")         -->
+<!ENTITY dollar   "&#36;"><!-- U+0024 DOLLAR SIGN                       ("$")         -->
+<!ENTITY percnt   "&#37;"><!-- U+0025 PERCENT SIGN                      ("%")         -->
+<!ENTITY lpar     "&#40;"><!-- U+0028 LEFT PARENTHESIS                  ("(")         -->
+<!ENTITY rpar     "&#41;"><!-- U+0029 RIGHT PARENTHESIS                 (")")         -->
+<!ENTITY ast      "&#42;"><!-- U+002A ASTERISK                          ("*")         -->
+<!ENTITY plus     "&#43;"><!-- U+002B PLUS SIGN                         ("+")         -->
+<!ENTITY comma    "&#44;"><!-- U+002C COMMA                             (",")         -->
+<!ENTITY hyphen   "&#45;"><!-- U+002D HYPHEN-MINUS                      ("-")         -->
+<!ENTITY period   "&#46;"><!-- U+002E FULL STOP                         (".")         -->
+<!ENTITY sol      "&#47;"><!-- U+002F SOLIDUS                           ("/")         -->
+<!ENTITY colon    "&#58;"><!-- U+003A COLON                             (":")         -->
+<!ENTITY semi     "&#59;"><!-- U+003B SEMICOLON                         (";")         -->
+<!ENTITY equals   "&#61;"><!-- U+003D EQUALS SIGN                       ("=")         -->
+<!ENTITY quest    "&#63;"><!-- U+003F QUESTION MARK                     ("?")         -->
+<!ENTITY commat   "&#64;"><!-- U+0040 COMMERCIAL AT                     ("@")         -->
+<!ENTITY lsqb     "&#91;"><!-- U+005B LEFT SQUARE BRACKET               ("[")         -->
+<!ENTITY bsol     "&#92;"><!-- U+005C REVERSE SOLIDUS                   ("\\")        -->
+<!ENTITY rsqb     "&#93;"><!-- U+005D RIGHT SQUARE BRACKET              ("]")         -->
+<!ENTITY circ     "&#94;"><!-- U+005E CIRCUMFLEX ACCENT                 ("^")         -->
+<!ENTITY lowbar   "&#95;"><!-- U+005F LOW LINE                          ("_")         -->
+<!ENTITY grave    "&#96;"><!-- U+0060 GRAVE ACCENT                      ("`")         -->
+<!ENTITY lcub    "&#123;"><!-- U+007B LEFT CURLY BRACKET                ("{")         -->
+<!ENTITY verbar  "&#124;"><!-- U+007C VERTICAL LINE                     ("|")         -->
+<!ENTITY rcub    "&#125;"><!-- U+007D RIGHT CURLY BRACKET               ("}")         -->
+
+<!-- Useful Unicode -->
+<!ENTITY Zcaron  "&#381;"><!-- U+017D LATIN CAPITAL LETTER Z WITH CARON ("Z")         -->
+<!ENTITY zcaron  "&#382;"><!-- U+017E LATIN SMALL LETTER Z WITH CARON   ("z")         -->
+<!ENTITY dash   "&#8208;"><!-- U+2010 HYPHEN                            ("-")         -->
+<!ENTITY nbhy   "&#8209;"><!-- U+2011 NON-BREAKING HYPHEN               (special "-") -->
+<!ENTITY wj     "&#8288;"><!-- U+2060 WORD JOINER                       (special "")  -->
+
+<!-- EOF -->
diff --git a/test/fixtures/cache-tests/spec/lib/rfc2629-xhtml.ent b/test/fixtures/cache-tests/spec/lib/rfc2629-xhtml.ent
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/rfc2629-xhtml.ent
@@ -0,0 +1,165 @@
+<!-- rfc2629-xhtml.ent
+
+     Character entity set for RFC-2629 source xml documents.
+     There is no need to invoke this directly from
+     the source document itself; just invoke the DTD.
+
+     These are known to (X)HTML, so any converter to these
+     formats can just leave them as is without having
+     to invoke this file from the output file.
+
+     Conversion to txt or nroff format will replace
+     these entities by the parenthesized text.
+
+     Typical invocation:
+       <!ENTITY % rfc2629-xhtml
+                PUBLIC "-//IETF//ENTITIES XHTML subset for RFC 2629//EN"
+                       "http://xml.resource.org/authoring/rfc2629-xhtml.ent">
+       %rfc2629-xhtml;
+-->
+
+<!-- All of ISO Latin 1 -->
+<!ENTITY nbsp    "&#160;"><!-- U+00A0 NO-BREAK SPACE                             (special " ")        -->
+<!ENTITY iexcl   "&#161;"><!-- U+00A1 INVERTED EXCLAMATION MARK                  ("!")                -->
+<!ENTITY cent    "&#162;"><!-- U+00A2 CENT SIGN                                  ("[cents]")          -->
+<!ENTITY pound   "&#163;"><!-- U+00A3 POUND SIGN                                 ("GBP")              -->
+<!ENTITY curren  "&#164;"><!-- U+00A4 CURRENCY SIGN                              ("[currency units]") -->
+<!ENTITY yen     "&#165;"><!-- U+00A5 YEN SIGN                                   ("JPY")              -->
+<!ENTITY brvbar  "&#166;"><!-- U+00A6 BROKEN BAR                                 ("|")                -->
+<!ENTITY sect    "&#167;"><!-- U+00A7 SECTION SIGN                               ("S.")               -->
+<!ENTITY uml     "&#168;"><!-- U+00A8 DIAERESIS                                  ('"')                -->
+<!ENTITY copy    "&#169;"><!-- U+00A9 COPYRIGHT SIGN                             ("(C)")              -->
+<!ENTITY ordf    "&#170;"><!-- U+00AA FEMININE ORDINAL INDICATOR                 ("a")                -->
+<!ENTITY laquo   "&#171;"><!-- U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK  ("<<")               -->
+<!ENTITY not     "&#172;"><!-- U+00AC NOT SIGN                                   ("[not]")            -->
+<!ENTITY shy     "&#173;"><!-- U+00AD SOFT HYPHEN                                (ignored "")         -->
+<!ENTITY reg     "&#174;"><!-- U+00AE REGISTERED SIGN                            ("(R)")              -->
+<!ENTITY macr    "&#175;"><!-- U+00AF MACRON                                     ("_")                -->
+<!ENTITY deg     "&#176;"><!-- U+00B0 DEGREE SIGN                                ("o")                -->
+<!ENTITY plusmn  "&#177;"><!-- U+00B1 PLUS-MINUS SIGN                            ("+/-")              -->
+<!ENTITY sup2    "&#178;"><!-- U+00B2 SUPERSCRIPT TWO                            ("^2")               -->
+<!ENTITY sup3    "&#179;"><!-- U+00B3 SUPERSCRIPT THREE                          ("^3")               -->
+<!ENTITY acute   "&#180;"><!-- U+00B4 ACUTE ACCENT                               ("'")                -->
+<!ENTITY micro   "&#181;"><!-- U+00B5 MICRO SIGN                                 ("[micro]")          -->
+<!ENTITY para    "&#182;"><!-- U+00B6 PILCROW SIGN                               ("P.")               -->
+<!ENTITY middot  "&#183;"><!-- U+00B7 MIDDLE DOT                                 (".")                -->
+<!ENTITY cedil   "&#184;"><!-- U+00B8 CEDILLA                                    (",")                -->
+<!ENTITY sup1    "&#185;"><!-- U+00B9 SUPERSCRIPT ONE                            ("^1")               -->
+<!ENTITY ordm    "&#186;"><!-- U+00BA MASCULINE ORDINAL INDICATOR                ("o")                -->
+<!ENTITY raquo   "&#187;"><!-- U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK (">>")               -->
+<!ENTITY frac14  "&#188;"><!-- U+00BC VULGAR FRACTION ONE QUARTER                ("1/4")              -->
+<!ENTITY frac12  "&#189;"><!-- U+00BD VULGAR FRACTION ONE HALF                   ("1/2")              -->
+<!ENTITY frac34  "&#190;"><!-- U+00BE VULGAR FRACTION THREE QUARTERS             ("3/4")              -->
+<!ENTITY iquest  "&#191;"><!-- U+00BF INVERTED QUESTION MARK                     ("?")                -->
+<!ENTITY Agrave  "&#192;"><!-- U+00C0 LATIN CAPITAL LETTER A WITH GRAVE          ("A")                -->
+<!ENTITY Aacute  "&#193;"><!-- U+00C1 LATIN CAPITAL LETTER A WITH ACUTE          ("A")                -->
+<!ENTITY Acirc   "&#194;"><!-- U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX     ("A")                -->
+<!ENTITY Atilde  "&#195;"><!-- U+00C3 LATIN CAPITAL LETTER A WITH TILDE          ("A")                -->
+<!ENTITY Auml    "&#196;"><!-- U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS      ("Ae")               -->
+<!ENTITY Aring   "&#197;"><!-- U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE     ("Ae")               -->
+<!ENTITY AElig   "&#198;"><!-- U+00C6 LATIN CAPITAL LETTER AE                    ("AE")               -->
+<!ENTITY Ccedil  "&#199;"><!-- U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA        ("C")                -->
+<!ENTITY Egrave  "&#200;"><!-- U+00C8 LATIN CAPITAL LETTER E WITH GRAVE          ("E")                -->
+<!ENTITY Eacute  "&#201;"><!-- U+00C9 LATIN CAPITAL LETTER E WITH ACUTE          ("E")                -->
+<!ENTITY Ecirc   "&#202;"><!-- U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX     ("E")                -->
+<!ENTITY Euml    "&#203;"><!-- U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS      ("E")                -->
+<!ENTITY Igrave  "&#204;"><!-- U+00CC LATIN CAPITAL LETTER I WITH GRAVE          ("I")                -->
+<!ENTITY Iacute  "&#205;"><!-- U+00CD LATIN CAPITAL LETTER I WITH ACUTE          ("I")                -->
+<!ENTITY Icirc   "&#206;"><!-- U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX     ("I")                -->
+<!ENTITY Iuml    "&#207;"><!-- U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS      ("I")                -->
+<!ENTITY ETH     "&#208;"><!-- U+00D0 LATIN CAPITAL LETTER ETH                   ("[ETH]")            -->
+<!ENTITY Ntilde  "&#209;"><!-- U+00D1 LATIN CAPITAL LETTER N WITH TILDE          ("N")                -->
+<!ENTITY Ograve  "&#210;"><!-- U+00D2 LATIN CAPITAL LETTER O WITH GRAVE          ("O")                -->
+<!ENTITY Oacute  "&#211;"><!-- U+00D3 LATIN CAPITAL LETTER O WITH ACUTE          ("O")                -->
+<!ENTITY Ocirc   "&#212;"><!-- U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX     ("O")                -->
+<!ENTITY Otilde  "&#213;"><!-- U+00D5 LATIN CAPITAL LETTER O WITH TILDE          ("O")                -->
+<!ENTITY Ouml    "&#214;"><!-- U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS      ("Oe")               -->
+<!ENTITY times   "&#215;"><!-- U+00D7 MULTIPLICATION SIGN                        ("x")                -->
+<!ENTITY Oslash  "&#216;"><!-- U+00D8 LATIN CAPITAL LETTER O WITH STROKE         ("Oe")               -->
+<!ENTITY Ugrave  "&#217;"><!-- U+00D9 LATIN CAPITAL LETTER U WITH GRAVE          ("U")                -->
+<!ENTITY Uacute  "&#218;"><!-- U+00DA LATIN CAPITAL LETTER U WITH ACUTE          ("U")                -->
+<!ENTITY Ucirc   "&#219;"><!-- U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX     ("U")                -->
+<!ENTITY Uuml    "&#220;"><!-- U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS      ("Ue")               -->
+<!ENTITY Yacute  "&#221;"><!-- U+00DD LATIN CAPITAL LETTER Y WITH ACUTE          ("Y")                -->
+<!ENTITY THORN   "&#222;"><!-- U+00DE LATIN CAPITAL LETTER THORN                 ("[THORN]")          -->
+<!ENTITY szlig   "&#223;"><!-- U+00DF LATIN SMALL LETTER SHARP S                 ("ss")               -->
+<!ENTITY agrave  "&#224;"><!-- U+00E0 LATIN SMALL LETTER A WITH GRAVE            ("a")                -->
+<!ENTITY aacute  "&#225;"><!-- U+00E1 LATIN SMALL LETTER A WITH ACUTE            ("a")                -->
+<!ENTITY acirc   "&#226;"><!-- U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX       ("a")                -->
+<!ENTITY atilde  "&#227;"><!-- U+00E3 LATIN SMALL LETTER A WITH TILDE            ("a")                -->
+<!ENTITY auml    "&#228;"><!-- U+00E4 LATIN SMALL LETTER A WITH DIAERESIS        ("ae")               -->
+<!ENTITY aring   "&#229;"><!-- U+00E5 LATIN SMALL LETTER A WITH RING ABOVE       ("ae")               -->
+<!ENTITY aelig   "&#230;"><!-- U+00E6 LATIN SMALL LETTER AE                      ("ae")               -->
+<!ENTITY ccedil  "&#231;"><!-- U+00E7 LATIN SMALL LETTER C WITH CEDILLA          ("c")                -->
+<!ENTITY egrave  "&#232;"><!-- U+00E8 LATIN SMALL LETTER E WITH GRAVE            ("e")                -->
+<!ENTITY eacute  "&#233;"><!-- U+00E9 LATIN SMALL LETTER E WITH ACUTE            ("e")                -->
+<!ENTITY ecirc   "&#234;"><!-- U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX       ("e")                -->
+<!ENTITY euml    "&#235;"><!-- U+00EB LATIN SMALL LETTER E WITH DIAERESIS        ("e")                -->
+<!ENTITY igrave  "&#236;"><!-- U+00EC LATIN SMALL LETTER I WITH GRAVE            ("i")                -->
+<!ENTITY iacute  "&#237;"><!-- U+00ED LATIN SMALL LETTER I WITH ACUTE            ("i")                -->
+<!ENTITY icirc   "&#238;"><!-- U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX       ("i")                -->
+<!ENTITY iuml    "&#239;"><!-- U+00EF LATIN SMALL LETTER I WITH DIAERESIS        ("i")                -->
+<!ENTITY eth     "&#240;"><!-- U+00F0 LATIN SMALL LETTER ETH                     ("[eth]")            -->
+<!ENTITY ntilde  "&#241;"><!-- U+00F1 LATIN SMALL LETTER N WITH TILDE            ("n")                -->
+<!ENTITY ograve  "&#242;"><!-- U+00F2 LATIN SMALL LETTER O WITH GRAVE            ("o")                -->
+<!ENTITY oacute  "&#243;"><!-- U+00F3 LATIN SMALL LETTER O WITH ACUTE            ("o")                -->
+<!ENTITY ocirc   "&#244;"><!-- U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX       ("o")                -->
+<!ENTITY otilde  "&#245;"><!-- U+00F5 LATIN SMALL LETTER O WITH TILDE            ("o")                -->
+<!ENTITY ouml    "&#246;"><!-- U+00F6 LATIN SMALL LETTER O WITH DIAERESIS        ("oe")               -->
+<!ENTITY divide  "&#247;"><!-- U+00F7 DIVISION SIGN                              ("/")                -->
+<!ENTITY oslash  "&#248;"><!-- U+00F8 LATIN SMALL LETTER O WITH STROKE           ("oe")               -->
+<!ENTITY ugrave  "&#249;"><!-- U+00F9 LATIN SMALL LETTER U WITH GRAVE            ("u")                -->
+<!ENTITY uacute  "&#250;"><!-- U+00FA LATIN SMALL LETTER U WITH ACUTE            ("u")                -->
+<!ENTITY ucirc   "&#251;"><!-- U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX       ("u")                -->
+<!ENTITY uuml    "&#252;"><!-- U+00FC LATIN SMALL LETTER U WITH DIAERESIS        ("ue")               -->
+<!ENTITY yacute  "&#253;"><!-- U+00FD LATIN SMALL LETTER Y WITH ACUTE            ("y")                -->
+<!ENTITY thorn   "&#254;"><!-- U+00FE LATIN SMALL LETTER THORN                   ("[thorn]")          -->
+<!ENTITY yuml    "&#255;"><!-- U+00FF LATIN SMALL LETTER Y WITH DIAERESIS        ("y")                -->
+
+<!-- Some of ISO Latin 9 and 10 -->
+<!ENTITY OElig   "&#338;"><!-- U+0152 LATIN CAPITAL LIGATURE OE                  ("OE")               -->
+<!ENTITY oelig   "&#339;"><!-- U+0153 LATIN SMALL LIGATURE OE                    ("oe")               -->
+<!ENTITY Scaron  "&#352;"><!-- U+0160 LATIN CAPITAL LETTER S WITH CARON          ("S")                -->
+<!ENTITY scaron  "&#353;"><!-- U+0161 LATIN SMALL LETTER S WITH CARON            ("s")                -->
+<!ENTITY Yuml    "&#376;"><!-- U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS      ("Y")                -->
+
+<!-- Other Unicode (including some characters from the windows-1252 repertoire) -->
+<!ENTITY fnof    "&#402;"><!-- U+0192 LATIN SMALL LETTER F WITH HOOK             ("f")                -->
+<!ENTITY tilde   "&#732;"><!-- U+02DC SMALL TILDE                                ("~")                -->
+<!ENTITY ensp   "&#8194;"><!-- U+2002 EN SPACE                                   (" ")                -->
+<!ENTITY emsp   "&#8195;"><!-- U+2003 EM SPACE                                   (" ")                -->
+<!ENTITY thinsp "&#8201;"><!-- U+2009 THIN SPACE                                 (" ")                -->
+<!ENTITY ndash  "&#8211;"><!-- U+2013 EN DASH                                    ("-")                -->
+<!ENTITY mdash  "&#8212;"><!-- U+2014 EM DASH                                    ("-\u002D")          -->
+<!ENTITY lsquo  "&#8216;"><!-- U+2018 LEFT SINGLE QUOTATION MARK                 ("'")                -->
+<!ENTITY rsquo  "&#8217;"><!-- U+2019 RIGHT SINGLE QUOTATION MARK                ("'")                -->
+<!ENTITY sbquo  "&#8218;"><!-- U+201A SINGLE LOW-9 QUOTATION MARK                ("'")                -->
+<!ENTITY ldquo  "&#8220;"><!-- U+201C LEFT DOUBLE QUOTATION MARK                 ('"')                -->
+<!ENTITY rdquo  "&#8221;"><!-- U+201D RIGHT DOUBLE QUOTATION MARK                ('"')                -->
+<!ENTITY bdquo  "&#8222;"><!-- U+201E DOUBLE LOW-9 QUOTATION MARK                ('"')                -->
+<!ENTITY dagger "&#8224;"><!-- U+2020 DAGGER                                     ("*!*")              -->
+<!ENTITY Dagger "&#8225;"><!-- U+2021 DOUBLE DAGGER                              ("*!!*")             -->
+<!ENTITY bull   "&#8226;"><!-- U+2022 BULLET                                     ("o")                -->
+<!ENTITY hellip "&#8230;"><!-- U+2026 HORIZONTAL ELLIPSIS                        ("...")              -->
+<!ENTITY permil "&#8242;"><!-- U+2030 PER MILLE SIGN                             ("[/1000]")          -->
+<!ENTITY prime  "&#8242;"><!-- U+2032 PRIME                                      ("'")                -->
+<!ENTITY Prime  "&#8243;"><!-- U+2033 DOUBLE PRIME                               ('"')                -->
+<!ENTITY lsaquo "&#8249;"><!-- U+2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK  ("<")                -->
+<!ENTITY rsaquo "&#8250;"><!-- U+203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (">")                -->
+<!ENTITY frasl  "&#8260;"><!-- U+2044 FRACTION SLASH                             ("/")                -->
+<!ENTITY euro   "&#8364;"><!-- U+20AC EURO SIGN                                  ("EUR")              -->
+<!ENTITY trade  "&#8482;"><!-- U+2122 TRADE MARK SIGN                            ("[TM]")             -->
+<!ENTITY larr   "&#8592;"><!-- U+2190 LEFTWARDS ARROW                            ("<-\u002D")         -->
+<!ENTITY rarr   "&#8594;"><!-- U+2192 RIGHTWARDS ARROW                           ("\u002D->")         -->
+<!ENTITY harr   "&#8596;"><!-- U+2194 LEFT RIGHT ARROW                           ("<->")              -->
+<!ENTITY lArr   "&#8656;"><!-- U+21D0 LEFTWARDS DOUBLE ARROW                     ("<==")              -->
+<!ENTITY rArr   "&#8658;"><!-- U+21D2 RIGHTWARDS DOUBLE ARROW                    ("==>")              -->
+<!ENTITY hArr   "&#8660;"><!-- U+21D4 LEFT RIGHT DOUBLE ARROW                    ("<=>")              -->
+<!ENTITY minus  "&#8722;"><!-- U+2212 MINUS SIGN                                 ("-")                -->
+<!ENTITY lowast "&#8727;"><!-- U+2217 ASTERISK OPERATOR                          ("*")                -->
+<!ENTITY le     "&#8804;"><!-- U+2264 LESS-THAN OR EQUAL TO                      ("<=")               -->
+<!ENTITY ge     "&#8805;"><!-- U+2265 GREATER-THAN OR EQUAL TO                   (">=")               -->
+<!ENTITY lang   "&#9001;"><!-- U+2329 LEFT-POINTING ANGLE BRACKET                ("<")                -->
+<!ENTITY rang   "&#9002;"><!-- U+232A RIGHT-POINTING ANGLE BRACKET               (">")                -->
+
+<!-- EOF -->
diff --git a/test/fixtures/cache-tests/spec/lib/rfc2629.dtd b/test/fixtures/cache-tests/spec/lib/rfc2629.dtd
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/rfc2629.dtd
@@ -0,0 +1,312 @@
+<!--
+  revised DTD for the RFC document series, draft of 2009-10-06
+  -->
+
+
+<!--
+  Typical invocation:
+      <!DOCTYPE rfc PUBLIC "-//IETF//DTD RFC 2629//EN"
+                           "http://xml.resource.org/authoring/rfc2629.dtd" [
+        ... dtd subset ...
+      ]>
+    or
+      <!DOCTYPE rfc SYSTEM "rfc2629.dtd" [
+        ... dtd subset ...
+      ]>
+  -->
+
+
+<!--
+  Contents
+
+    Character entities
+
+    DTD data types
+
+    The top-level
+
+    Front matter
+
+    The Body
+
+    Back matter
+  -->
+
+
+<!--
+  Character entities
+  -->
+
+
+<!ENTITY % rfc2629-xhtml
+         PUBLIC "-//IETF//ENTITIES XHTML subset for RFC 2629//EN"
+                "rfc2629-xhtml.ent">
+%rfc2629-xhtml;
+
+<!ENTITY % rfc2629-other
+         PUBLIC "-//IETF//ENTITIES Other for RFC 2629//EN"
+                "rfc2629-other.ent">
+%rfc2629-other;
+
+
+<!--
+  DTD data types:
+
+        entity        description
+        ======        ===============================================
+        NUMBER        [0-9]+
+        NUMBERS       a comma-separated list of NUMBER
+
+        DAY           the day of the month, e.g., "1"
+        MONTH         the month of the year, e.g., "January"
+        YEAR          a four-digit year, e.g., "1999"
+
+        URI           e.g., "http://invisible.net/"
+
+        ATEXT/CTEXT   printable ASCII text (no line-terminators)
+
+        TEXT          character data
+  -->
+
+
+<!ENTITY % NUMBER     "CDATA">
+<!ENTITY % NUMBERS    "CDATA">
+
+<!ENTITY % DAY        "CDATA">
+<!ENTITY % MONTH      "CDATA">
+<!ENTITY % YEAR       "CDATA">
+
+<!ENTITY % URI        "CDATA">
+
+<!ENTITY % ATEXT      "CDATA">
+<!ENTITY % CTEXT      "#PCDATA">
+
+<!ENTITY % TEXT       "#PCDATA">
+
+<!ENTITY   rfc.number "XXXX">
+
+
+<!--
+  The top-level
+  -->
+
+
+<!--
+  attributes for the "rfc" element are supplied by the RFC
+  editor. when preparing drafts, authors should leave them blank.
+
+  the "seriesNo" attribute is used if the category is, e.g., BCP.
+  -->
+<!ELEMENT rfc         (front,middle,back?)>
+<!ATTLIST rfc
+          number      %NUMBER;           #IMPLIED
+          obsoletes   %NUMBERS;          ""
+          updates     %NUMBERS;          ""
+          category    (std|bcp|info|exp|historic)
+                                         #IMPLIED
+          consensus   (no|yes)           #IMPLIED
+          seriesNo    %NUMBER;           #IMPLIED
+          ipr         (full2026|noDerivativeWorks2026|none
+                      |full3667|noModification3667|noDerivatives3667
+                      |full3978|noModification3978|noDerivatives3978
+                      |trust200811|noModificationTrust200811|noDerivativesTrust200811
+                      |trust200902|noModificationTrust200902|noDerivativesTrust200902
+                      |pre5378Trust200902)
+                                         #IMPLIED
+          iprExtract  IDREF              #IMPLIED
+          submissionType
+                      (IETF|IAB|IRTF|independent) "IETF"
+          docName     %ATEXT;            #IMPLIED
+          xml:lang    %ATEXT;            "en">
+
+
+<!--
+  Front matter
+  -->
+
+
+<!ELEMENT front       (title,author+,date,area*,workgroup*,keyword*,
+                       abstract?,note*)>
+
+<!-- the "abbrev" attribute is used for headers, etc. -->
+<!ELEMENT title       (%CTEXT;)>
+<!ATTLIST title
+          abbrev      %ATEXT;            #IMPLIED> 
+
+<!ELEMENT author      (organization?,address?)>
+<!ATTLIST author
+          initials    %ATEXT;            #IMPLIED
+          surname     %ATEXT;            #IMPLIED
+          fullname    %ATEXT;            #IMPLIED
+          role        (editor)           #IMPLIED>
+
+<!ELEMENT organization
+                      (%CTEXT;)>
+<!ATTLIST organization
+          abbrev      %ATEXT;            #IMPLIED> 
+ 
+<!ELEMENT address     (postal?,phone?,facsimile?,email?,uri?)>
+
+<!-- this content model should be more strict:
+     at most one of each the city, region, code, and country
+     elements may be present -->
+<!ELEMENT postal      (street+,(city|region|code|country)*)>
+<!ELEMENT street      (%CTEXT;)>
+<!ELEMENT city        (%CTEXT;)>
+<!ELEMENT region      (%CTEXT;)>
+<!ELEMENT code        (%CTEXT;)>
+<!ELEMENT country     (%CTEXT;)>
+<!ELEMENT phone       (%CTEXT;)>
+<!ELEMENT facsimile   (%CTEXT;)>
+<!ELEMENT email       (%CTEXT;)>
+<!ELEMENT uri         (%CTEXT;)>
+
+<!ELEMENT date        EMPTY>
+<!ATTLIST date
+          day         %DAY;              #IMPLIED
+          month       %MONTH;            #IMPLIED
+          year        %YEAR;             #IMPLIED>
+
+<!-- meta-data... -->
+<!ELEMENT area        (%CTEXT;)>
+<!ELEMENT workgroup   (%CTEXT;)>
+<!ELEMENT keyword     (%CTEXT;)>
+
+<!ELEMENT abstract    (t)+>
+<!ELEMENT note        (t)+>
+<!ATTLIST note
+          title       %ATEXT;            #REQUIRED>
+
+
+<!--
+  The body
+  -->
+
+
+<!ELEMENT middle      (section+)>
+
+<!ELEMENT section     ((t|figure|texttable|iref)*,section*)>
+<!ATTLIST section
+          anchor      ID                 #IMPLIED
+          title       %ATEXT;            #REQUIRED
+          toc         (include|exclude|default)
+                                         "default">
+
+<!-- use of <appendix/> is deprecated...
+<!ELEMENT appendix    ((t|figure|texttable|iref)*,appendix*)>
+<!ATTLIST appendix
+          anchor      ID                 #IMPLIED
+          title       %ATEXT;            #REQUIRED
+          toc         (include|exclude|default)
+                                         "default">
+  -->
+
+<!-- use of <figure/> is deprecated... -->
+<!ELEMENT t           (%TEXT;|list|figure|xref|eref|iref|cref|spanx|vspace)*>
+<!ATTLIST t
+          anchor      ID                 #IMPLIED
+          hangText    %ATEXT;            #IMPLIED>
+
+<!-- the value of the style attribute is inherited from the closest 
+     parent -->
+<!ELEMENT list        (t+)>
+<!ATTLIST list
+          style       %ATEXT;            #IMPLIED
+          hangIndent  %NUMBER;           #IMPLIED
+          counter     %ATEXT;            #IMPLIED>
+
+<!ELEMENT xref        (%CTEXT;)>
+<!ATTLIST xref
+          target      IDREF              #REQUIRED
+          pageno      (true|false)       "false"
+          format      (counter|title|none|default)
+                                         "default">
+
+<!ELEMENT eref        (%CTEXT;)>
+<!ATTLIST eref
+          target      %URI;              #REQUIRED>
+
+<!ELEMENT iref        EMPTY>
+<!ATTLIST iref
+          item        %ATEXT;            #REQUIRED
+          subitem     %ATEXT;            ""
+          primary    (true|false)       "false">
+
+<!ELEMENT cref        (%CTEXT;)>
+<!ATTLIST cref
+          anchor      ID                 #IMPLIED
+          source      %ATEXT;            #IMPLIED>
+
+<!ELEMENT spanx       (%CTEXT;)>
+<!ATTLIST spanx
+          xml:space   (default|preserve) "preserve"
+          style       %ATEXT;            "emph">
+
+<!ELEMENT vspace      EMPTY>
+<!ATTLIST vspace
+          blankLines  %NUMBER;           "0">
+
+<!ELEMENT figure      (iref*,preamble?,artwork,postamble?)>
+<!ATTLIST figure
+          anchor      ID                 #IMPLIED
+          title       %ATEXT;            ""
+          suppress-title (true|false)    "false"
+          src         %URI;              #IMPLIED
+          align       (left|center|right) "left"
+          alt         %ATEXT;            ""
+          width       %ATEXT;            ""
+          height      %ATEXT;            "">
+
+<!ELEMENT preamble    (%TEXT;|xref|eref|iref|cref|spanx)*>
+<!ELEMENT artwork     (%TEXT;)*>
+<!ATTLIST artwork
+          xml:space   (default|preserve) "preserve"
+          name        %ATEXT;            ""
+          type        %ATEXT;            ""
+          src         %URI;              #IMPLIED
+          align       (left|center|right) "left"
+          alt         %ATEXT;            ""
+          width       %ATEXT;            ""
+          height      %ATEXT;            "">
+
+<!ELEMENT postamble   (%TEXT;|xref|eref|iref|cref|spanx)*>
+    
+<!ELEMENT texttable   (preamble?,ttcol+,c*,postamble?)>
+<!ATTLIST texttable
+          anchor      ID                 #IMPLIED
+          title       %ATEXT;            ""
+          suppress-title (true|false)    "false"
+          align       (left|center|right) "center"
+          style       (all|none|headers|full) "full">
+<!ELEMENT ttcol       (%CTEXT;)>
+<!ATTLIST ttcol
+          width       %ATEXT;            #IMPLIED
+          align       (left|center|right) "left">
+<!ELEMENT c           (%TEXT;|xref|eref|iref|cref|spanx)*>
+
+
+<!--
+  Back matter
+  -->
+
+
+<!-- sections, if present, are appendices -->
+<!ELEMENT back        (references*,section*)>
+
+<!ELEMENT references  (reference+)>
+<!ATTLIST references
+          title       %ATEXT;            "References">
+<!ELEMENT reference   (front,seriesInfo*,format*,annotation*)>
+<!ATTLIST reference
+          anchor      ID                 #IMPLIED
+          target      %URI;              #IMPLIED>
+<!ELEMENT seriesInfo  EMPTY>
+<!ATTLIST seriesInfo
+          name        %ATEXT;            #REQUIRED
+          value       %ATEXT;            #REQUIRED>
+<!ELEMENT format      EMPTY>
+<!ATTLIST format
+          target      %URI;              #IMPLIED
+          type        %ATEXT;            #REQUIRED
+          octets      %NUMBER;           #IMPLIED>
+<!ELEMENT annotation  (%TEXT;|xref|eref|iref|cref|spanx)*>
diff --git a/test/fixtures/cache-tests/spec/lib/rfc2629.xslt b/test/fixtures/cache-tests/spec/lib/rfc2629.xslt
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/rfc2629.xslt
@@ -0,0 +1,10226 @@
+<!--
+    XSLT transformation from RFC2629 XML format to HTML
+
+    Copyright (c) 2006-2017, Julian Reschke (julian.reschke@greenbytes.de)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Julian Reschke nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="2.0"
+
+                xmlns:date="http://exslt.org/dates-and-times"
+                xmlns:ed="http://greenbytes.de/2002/rfcedit"
+                xmlns:exslt="http://exslt.org/common"
+                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
+                xmlns:myns="mailto:julian.reschke@greenbytes.de?subject=rcf2629.xslt"
+                xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+                xmlns:saxon="http://saxon.sf.net/"
+                xmlns:saxon-old="http://icl.com/saxon"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:x="http://purl.org/net/xml2rfc/ext"
+                xmlns:xi="http://www.w3.org/2001/XInclude"
+                xmlns:xhtml="http://www.w3.org/1999/xhtml"
+
+                exclude-result-prefixes="date ed exslt msxsl myns rdf saxon saxon-old svg x xi xhtml"
+                >
+
+<xsl:strip-space elements="abstract address author back figure front list middle note postal reference references rfc section table tbody thead tr texttable"/>
+
+<xsl:output method="html" encoding="utf-8" doctype-system="about:legacy-compat" indent="no"/>
+
+<!-- PIs outside the root element, or inside the root element but before <front> -->
+<xsl:variable name="global-std-pis" select="/processing-instruction('rfc') | /*/processing-instruction('rfc')[following-sibling::front]"/>
+
+<!-- rfc authorship PI -->
+
+<xsl:param name="xml2rfc-authorship">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'authorship'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc comments PI -->
+
+<xsl:param name="xml2rfc-comments">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'comments'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc compact PI -->
+
+<xsl:param name="xml2rfc-compact">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'compact'"/>
+    <xsl:with-param name="default" select="$xml2rfc-rfcedstyle"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc footer PI -->
+
+<xsl:param name="xml2rfc-footer">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'footer'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc header PI -->
+
+<xsl:param name="xml2rfc-header">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'header'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- rfc inline PI -->
+
+<xsl:param name="xml2rfc-inline">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'inline'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- include a table of contents if a processing instruction <?rfc?>
+     exists with contents toc="yes". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-toc">
+  <xsl:variable name="default">
+    <xsl:choose>
+      <xsl:when test="/rfc/@version >= 3">yes</xsl:when>
+      <xsl:otherwise>no</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="/rfc/@tocInclude='false'">no</xsl:when>
+    <xsl:when test="/rfc/@tocInclude='true'">yes</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'toc'"/>
+        <xsl:with-param name="default" select="$default"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- optional tocdepth-->
+
+<xsl:param name="xml2rfc-tocdepth">
+  <xsl:choose>
+    <xsl:when test="/rfc/@tocDepth">
+      <xsl:value-of select="/rfc/@tocDepth"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'tocdepth'"/>
+        <xsl:with-param name="default" select="'3'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:variable name="parsedTocDepth">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-tocdepth='1'">1</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='2'">2</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='3'">3</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='4'">4</xsl:when>
+    <xsl:when test="$xml2rfc-tocdepth='5'">5</xsl:when>
+    <xsl:otherwise>99</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- suppress top block if a processing instruction <?rfc?>
+     exists with contents tocblock="no". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-topblock">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'topblock'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- Format to the RFC Editor's taste -->
+
+<xsl:param name="xml2rfc-rfcedstyle">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'rfcedstyle'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- the name of an automatically inserted references section -->
+
+<xsl:param name="xml2rfc-refparent">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'refparent'"/>
+    <xsl:with-param name="default" select="'References'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- use symbolic reference names instead of numeric ones unless a processing instruction <?rfc?>
+     exists with contents symrefs="no". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-symrefs">
+  <xsl:choose>
+    <xsl:when test="/rfc/@symRefs='false'">no</xsl:when>
+    <xsl:when test="/rfc/@symRefs='true'">yes</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'symrefs'"/>
+        <xsl:with-param name="default" select="'yes'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- sort references if a processing instruction <?rfc?>
+     exists with contents sortrefs="yes". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-sortrefs">
+  <xsl:choose>
+    <xsl:when test="/rfc/@sortRefs='true'">yes</xsl:when>
+    <xsl:when test="/rfc/@sortRefs='false'">no</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$global-std-pis"/>
+        <xsl:with-param name="attr" select="'sortrefs'"/>
+        <xsl:with-param name="default" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- insert editing marks if a processing instruction <?rfc?>
+     exists with contents editing="yes". Can be overridden by an XSLT parameter -->
+
+<xsl:param name="xml2rfc-editing">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'editing'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- make it a private paper -->
+
+<xsl:param name="xml2rfc-private">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'private'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- background image? -->
+
+<xsl:param name="xml2rfc-background">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'background'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- CSS max page width -->
+
+<xsl:param name="xml2rfc-ext-maxwidth">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'maxwidth'"/>
+    <xsl:with-param name="default" select="'1000'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:variable name="parsedMaxwidth">
+  <xsl:choose>
+    <xsl:when test="string(number($xml2rfc-ext-maxwidth)) != 'NaN'">
+      <xsl:value-of select="$xml2rfc-ext-maxwidth"/>
+    </xsl:when>
+    <xsl:when test="$xml2rfc-ext-maxwidth='none'"></xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('Unsupported value of rfc-ext maxwidth PI: ', $xml2rfc-ext-maxwidth)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- CSS styles -->
+
+<xsl:param name="xml2rfc-ext-styles">fft-sans-serif ffb-serif ff-cleartype</xsl:param>
+
+<xsl:variable name="styles" select="concat(' ',normalize-space($xml2rfc-ext-styles),' ')"/>
+
+<xsl:param name="xml2rfc-ext-ff-body">
+  <xsl:variable name="t">
+    <xsl:if test="contains($styles,' ff-noto ')">
+      <xsl:if test="contains($styles,' ffb-serif ')">
+        'Noto Serif', 
+      </xsl:if>
+      <xsl:if test="contains($styles,' ffb-sans-serif ')">
+        'Noto Sans',
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ff-cleartype ')">
+      <xsl:if test="contains($styles,' ffb-serif ')">
+        cambria, georgia, 
+      </xsl:if>
+      <xsl:if test="contains($styles,' ffb-sans-serif ')">
+        candara, calibri,
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ffb-sans-serif ')">
+      segoe, optima, arial, sans-serif,
+    </xsl:if>
+    serif
+  </xsl:variable>
+  <xsl:call-template name="ff-list">
+    <xsl:with-param name="s" select="normalize-space($t)"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-ff-title">
+  <xsl:variable name="t">
+    <xsl:if test="contains($styles,' ff-noto ')">
+      <xsl:if test="contains($styles,' fft-serif ')">
+        'Noto Serif', 
+      </xsl:if>
+      <xsl:if test="contains($styles,' fft-sans-serif ')">
+        'Noto Sans',
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ff-cleartype ')">
+      <xsl:if test="contains($styles,' fft-serif ')">
+        cambria, georgia, 
+      </xsl:if>
+      <xsl:if test="contains($styles,' fft-sans-serif ')">
+        candara, calibri,
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="contains($styles,' fft-serif ')">
+      serif,
+    </xsl:if>
+    <xsl:if test="contains($styles,' fft-sans-serif ')">
+      segoe, optima, arial,
+    </xsl:if>
+    sans-serif
+  </xsl:variable>
+  <xsl:call-template name="ff-list">
+    <xsl:with-param name="s" select="normalize-space($t)"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-ff-pre">
+  <xsl:variable name="t">
+    <xsl:if test="contains($styles,' ff-noto ')">
+      'Roboto Mono',
+    </xsl:if>
+    <xsl:if test="contains($styles,' ff-cleartype ')">
+      consolas, monaco,
+    </xsl:if>
+    monospace
+  </xsl:variable>
+  <xsl:call-template name="ff-list">
+    <xsl:with-param name="s" select="normalize-space($t)"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-webfonts">
+  <xsl:if test="contains($styles,' ff-noto ')">
+    <xsl:if test="contains($styles,' ffb-sans-serif ') or contains($styles,' fft-sans-serif ')">
+      <xsl:text>@import url('https://fonts.googleapis.com/css?family=Noto+Sans:r,b,i,bi');&#10;</xsl:text>
+    </xsl:if>
+    <xsl:if test="contains($styles,' ffb-serif ') or contains($styles,' fft-serif ')">
+      <xsl:text>@import url('https://fonts.googleapis.com/css?family=Noto+Serif:r,b,i,bi');&#10;</xsl:text>
+    </xsl:if>
+    <xsl:text>@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:r,b,i,bi');&#10;</xsl:text>
+  </xsl:if>
+</xsl:param>
+
+<xsl:template name="ff-list">
+  <xsl:param name="s"/>
+  <xsl:choose>
+    <xsl:when test="not(contains($s,','))">
+      <xsl:value-of select="normalize-space($s)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="normalize-space(substring-before($s,','))"/>
+      <xsl:text>, </xsl:text>
+      <xsl:call-template name="ff-list">
+        <xsl:with-param name="s" select="substring-after($s,',')"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- include PI -->
+
+<xsl:template name="getIncludes">
+  <xsl:param name="nodes"/>
+  <xsl:for-each select="$nodes">
+    <xsl:variable name="include">
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="."/>
+        <xsl:with-param name="attr" select="'include'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="doc">
+      <xsl:choose>
+        <xsl:when test="$include=''"/>
+        <xsl:when test="substring($include, string-length($include) - 3) != '.xml'">
+          <xsl:copy-of select="document(concat($include,'.xml'))"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:copy-of select="document($include)"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="count(exslt:node-set($doc)) = 1">
+      <myns:include from="{$include}" in="{generate-id(..)}">
+        <xsl:copy-of select="$doc"/>
+      </myns:include>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="getXIncludes">
+  <xsl:param name="nodes"/>
+  <xsl:for-each select="$nodes">
+    <xsl:choose>
+      <xsl:when test="(@parse and @parse!='xml') or @xpointer">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="'Unsupported attributes on x:include element'"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="doc">
+          <xsl:copy-of select="document(@href)"/>
+        </xsl:variable>
+        <xsl:if test="count(exslt:node-set($doc)) = 1">
+          <myns:include from="{@href}" in="{generate-id(..)}">
+            <xsl:copy-of select="$doc"/>
+          </myns:include>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:variable name="includeDirectives">
+  <xsl:call-template name="getIncludes">
+    <xsl:with-param name="nodes" select="/rfc/back/references/processing-instruction('rfc')"/>
+  </xsl:call-template>
+  <xsl:call-template name="getXIncludes">
+    <xsl:with-param name="nodes" select="/rfc/back/references/xi:include"/>
+  </xsl:call-template>
+</xsl:variable>
+
+<!-- prettyprinting -->
+
+<xsl:param name="xml2rfc-ext-html-pretty-print">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'html-pretty-print'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:variable name="prettyprint-class">
+  <xsl:if test="$xml2rfc-ext-html-pretty-print">
+    <xsl:value-of select="substring-before(normalize-space($xml2rfc-ext-html-pretty-print),' ')"/>
+  </xsl:if>
+</xsl:variable>
+
+<xsl:variable name="prettyprint-script">
+  <xsl:if test="$xml2rfc-ext-html-pretty-print">
+    <xsl:value-of select="substring-after(normalize-space($xml2rfc-ext-html-pretty-print),' ')"/>
+  </xsl:if>
+</xsl:variable>
+
+<!-- external resource containing errata, as generated by parse-errata.xslt -->
+<xsl:param name="xml2rfc-ext-errata"/>
+<xsl:variable name="errata-parsed" select="document($xml2rfc-ext-errata)//erratum[@status!='Rejected']"/>
+
+<!-- "remove in RFC phrases" -->
+<xsl:variable name="note-removeInRFC">This note is to be removed before publishing as an RFC.</xsl:variable>
+<xsl:variable name="section-removeInRFC">This section is to be removed before publishing as an RFC.</xsl:variable>
+
+
+<!-- CSS class name remapping -->
+
+<xsl:param name="xml2rfc-ext-css-map"/>
+
+<xsl:template name="generate-css-class">
+  <xsl:param name="name"/>
+  <xsl:variable name="cssmap" select="document($xml2rfc-ext-css-map)"/>
+  <xsl:variable name="entry" select="$cssmap/*/map[@from=$name]"/>
+  <xsl:choose>
+    <xsl:when test="$entry">
+      <xsl:value-of select="$entry/@css"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$name"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- WORK IN PROGRESS; ONLY A FEW CLASSES SUPPORTED FOR NOW -->
+<xsl:variable name="css-artwork"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'artwork'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-art-svg"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'art-svg'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-docstatus"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'docstatus'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-center"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'center'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-erratum"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'erratum'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-error"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'error'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-fbbutton"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'fbbutton'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-feedback"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'feedback'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-header"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'header'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-left"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'left'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-noprint"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'noprint'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-publishedasrfc"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'publishedasrfc'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-reference"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'reference'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-right"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'right'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tcenter"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tcenter'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tleft"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tleft'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tright"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tright'"/></xsl:call-template></xsl:variable>
+<xsl:variable name="css-tt"><xsl:call-template name="generate-css-class"><xsl:with-param name="name" select="'tt'"/></xsl:call-template></xsl:variable>
+
+
+<!-- RFC-Editor site linking -->
+
+<xsl:param name="xml2rfc-ext-link-rfc-to-info-page">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'link-rfc-to-info-page'"/>
+    <xsl:with-param name="default">
+      <xsl:choose>
+        <xsl:when test="$pub-yearmonth >= 201503">yes</xsl:when>
+        <xsl:otherwise>no</xsl:otherwise>
+      </xsl:choose>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- DOI insertion -->
+
+<xsl:param name="xml2rfc-ext-insert-doi">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'insert-doi'"/>
+    <xsl:with-param name="default">
+      <xsl:choose>
+        <xsl:when test="$pub-yearmonth >= 201505">yes</xsl:when>
+        <xsl:otherwise>no</xsl:otherwise>
+      </xsl:choose>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- initials handling? -->
+
+<xsl:param name="xml2rfc-multiple-initials">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc')"/>
+    <xsl:with-param name="attr" select="'multiple-initials'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- paragraph links? -->
+
+<xsl:param name="xml2rfc-ext-paragraph-links">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'paragraph-links'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for XML parsing in artwork -->
+
+<xsl:param name="xml2rfc-ext-parse-xml-in-artwork">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'parse-xml-in-artwork'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-trace-parse-xml">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'trace-parse-xml'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for excluding the index -->
+
+<xsl:param name="xml2rfc-ext-include-index">
+  <xsl:choose>
+    <xsl:when test="/rfc/@indexInclude='false'">no</xsl:when>
+    <xsl:when test="/rfc/@indexInclude='true'">yes</xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+        <xsl:with-param name="attr" select="'include-index'"/>
+        <xsl:with-param name="default" select="'yes'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- extension for inserting RFC metadata -->
+
+<xsl:param name="xml2rfc-ext-insert-metadata">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'insert-metadata'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for excluding DCMI properties in meta tag (RFC2731) -->
+
+<xsl:param name="xml2rfc-ext-support-rfc2731">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'support-rfc2731'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for specifying the value for <vspace> after which it's taken as a page break -->
+
+<xsl:param name="xml2rfc-ext-vspace-pagebreak">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'vspace-pagebreak'"/>
+    <xsl:with-param name="default" select="'100'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for allowing markup inside artwork -->
+
+<xsl:param name="xml2rfc-ext-allow-markup-in-artwork">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'allow-markup-in-artwork'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- extension for including references into index -->
+
+<xsl:param name="xml2rfc-ext-include-references-in-index">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'include-references-in-index'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- position of author's section -->
+
+<xsl:param name="xml2rfc-ext-authors-section">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'authors-section'"/>
+    <xsl:with-param name="default" select="'end'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- justification? -->
+
+<xsl:param name="xml2rfc-ext-justification">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'justification'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- switch for doublesided layout -->
+
+<xsl:param name="xml2rfc-ext-duplex">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'duplex'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- trailing dots in section numbers -->
+
+<xsl:param name="xml2rfc-ext-sec-no-trailing-dots">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'sec-no-trailing-dots'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- check artwork width? -->
+
+<xsl:param name="xml2rfc-ext-check-artwork-width">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'check-artwork-width'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- choose whether or not to do mailto links -->
+
+<xsl:param name="xml2rfc-linkmailto">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'linkmailto'"/>
+    <xsl:with-param name="default" select="'yes'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- iprnotified switch -->
+
+<xsl:param name="xml2rfc-iprnotified">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="$global-std-pis"/>
+    <xsl:with-param name="attr" select="'iprnotified'"/>
+    <xsl:with-param name="default" select="'no'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- URL templates for RFCs and Internet Drafts. -->
+
+<!-- Reference the authoritative ASCII versions
+<xsl:param name="rfcUrlPrefix" select="'http://www.ietf.org/rfc/rfc'" />
+<xsl:param name="rfcUrlPostfix" select="'.txt'" />
+-->
+<!-- Reference the marked up versions over on https://tools.ietf.org/html. -->
+<xsl:param name="rfcUrlFragSection" select="'section-'" />
+<xsl:param name="rfcUrlFragAppendix" select="'appendix-'" />
+<xsl:param name="internetDraftUrlFragSection" select="'section-'" />
+<xsl:param name="internetDraftUrlFragAppendix" select="'appendix-'" />
+
+<!--templates for URI calculation -->
+
+<xsl:param name="xml2rfc-ext-isbn-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'isbn-uri'"/>
+    <xsl:with-param name="default">https://www.worldcat.org/search?q=isbn:{isbn}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-isbn-uri">
+  <xsl:param name="isbn"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-isbn-uri"/>
+    <xsl:with-param name="replace" select="'{isbn}'"/>
+    <xsl:with-param name="by" select="translate($isbn,'-','')"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-uri'"/>
+    <xsl:with-param name="default">https://tools.ietf.org/html/rfc{rfc}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-rfc-uri">
+  <xsl:param name="rfc"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-rfc-uri"/>
+    <xsl:with-param name="replace" select="'{rfc}'"/>
+    <xsl:with-param name="by" select="$rfc"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-internet-draft-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'internet-draft-uri'"/>
+    <xsl:with-param name="default">https://tools.ietf.org/html/{internet-draft}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-internet-draft-uri">
+  <xsl:param name="internet-draft"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-internet-draft-uri"/>
+    <xsl:with-param name="replace" select="'{internet-draft}'"/>
+    <xsl:with-param name="by" select="$internet-draft"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-doi-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'doi-uri'"/>
+    <xsl:with-param name="default">http://dx.doi.org/{doi}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-doi-uri">
+  <xsl:param name="doi"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-doi-uri"/>
+    <xsl:with-param name="replace" select="'{doi}'"/>
+    <xsl:with-param name="by" select="$doi"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-erratum-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-erratum-uri'"/>
+    <xsl:with-param name="default">https://www.rfc-editor.org/errata/eid{eid}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:template name="compute-rfc-erratum-uri">
+  <xsl:param name="eid"/>
+  <xsl:call-template name="replace-substring">
+    <xsl:with-param name="string" select="$xml2rfc-ext-rfc-erratum-uri"/>
+    <xsl:with-param name="replace" select="'{eid}'"/>
+    <xsl:with-param name="by" select="$eid"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:param name="xml2rfc-ext-rfc-errata-uri">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'rfc-errata-uri'"/>
+    <xsl:with-param name="default">https://www.rfc-editor.org/errata_search.php?rfc={rfc}</xsl:with-param>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- the format we're producing -->
+<xsl:param name="outputExtension" select="'html'"/>
+
+<!-- source for autorefresh -->
+<xsl:param name="xml2rfc-ext-refresh-from">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'refresh-from'"/>
+    <xsl:with-param name="default" select="''"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- XSLT for autorefresh -->
+<xsl:param name="xml2rfc-ext-refresh-xslt">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'refresh-xslt'"/>
+    <xsl:with-param name="default" select="'rfc2629.xslt'"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- interval for autorefresh -->
+<xsl:param name="xml2rfc-ext-refresh-interval">
+  <xsl:call-template name="parse-pis">
+    <xsl:with-param name="nodes" select="/processing-instruction('rfc-ext')"/>
+    <xsl:with-param name="attr" select="'refresh-interval'"/>
+    <xsl:with-param name="default" select="10"/>
+  </xsl:call-template>
+</xsl:param>
+
+<!-- warning re: absent node-set ext. function -->
+<xsl:variable name="node-set-warning">
+  This stylesheet requires either an XSLT-1.0 processor with node-set()
+  extension function, or an XSLT-2.0 processor. Therefore, parts of the
+  document couldn't be displayed.
+</xsl:variable>
+
+<!-- character translation tables -->
+<xsl:variable name="lcase" select="'abcdefghijklmnopqrstuvwxyz'" />
+<xsl:variable name="ucase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
+<xsl:variable name="digits" select="'0123456789'" />
+<xsl:variable name="alpha" select="concat($lcase,$ucase)"/>
+<xsl:variable name="alnum" select="concat($alpha,$digits)"/>
+
+<!-- build help keys for indices -->
+<xsl:key name="index-first-letter"
+  match="iref|reference"
+    use="translate(substring(concat(@anchor,@item),1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
+
+<xsl:key name="index-item"
+  match="iref"
+    use="@item" />
+
+<xsl:key name="index-item-subitem"
+  match="iref"
+    use="concat(@item,'..',@subitem)" />
+
+<xsl:key name="index-xref-by-sec"
+  match="xref[@x:sec|@section]"
+    use="concat(@target,'..',@x:sec,@section)" />
+
+<xsl:key name="index-xref-by-anchor"
+  match="xref[@x:rel|@relative]"
+    use="concat(@target,'..',@x:rel,@relative)" />
+
+<xsl:key name="anchor-item"
+  match="//*[@anchor]"
+    use="@anchor"/>
+
+<xsl:key name="xref-item"
+  match="//xref"
+    use="@target"/>
+
+<xsl:key name="extref-item"
+  match="//x:ref"
+    use="."/>
+
+<!-- prefix for automatically generated anchors -->
+<xsl:variable name="anchor-pref" select="'rfc.'" />
+
+<!-- IPR version switch -->
+<xsl:variable name="ipr-rfc3667" select="(
+  /rfc/@number &gt; 3708) or
+  not(
+    (/rfc/@ipr = 'full2026') or
+    (/rfc/@ipr = 'noDerivativeWorks2026') or
+    (/rfc/@ipr = 'noDerivativeWorksNow') or
+    (/rfc/@ipr = 'none') or
+    (/rfc/@ipr = '') or
+    not(/rfc/@ipr)
+  )" />
+
+<xsl:variable name="rfcno" select="/rfc/@number"/>
+
+<xsl:variable name="submissionType">
+  <xsl:choose>
+    <xsl:when test="/rfc/@submissionType='IETF' or not(/rfc/@submissionType) or /rfc/submissionType=''">IETF</xsl:when>
+    <xsl:when test="/rfc/@submissionType='IAB' or /rfc/@submissionType='IRTF' or /rfc/@submissionType='independent'">
+      <xsl:value-of select="/rfc/@submissionType"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat('(UNSUPPORTED SUBMISSION TYPE: ',/rfc/@submissionType,')')"/>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported value for /rfc/@submissionType: ', /rfc/@submissionType)"/>
+        <xsl:with-param name="inline" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <!-- sanity check on @consensus -->
+  <xsl:if test="/rfc/@consensus and /rfc/@submissionType='independent'">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg" select="concat('/rfc/@consensus meaningless with a /rfc/@submissionType value of ', /rfc/@submissionType)"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:variable>
+
+<xsl:variable name="consensus">
+  <xsl:choose>
+    <xsl:when test="/rfc/@consensus='yes' or /rfc/@consensus='true' or not(/rfc/@consensus)">yes</xsl:when>
+    <xsl:when test="/rfc/@consensus='no' or /rfc/@consensus='false'">no</xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat('(UNSUPPORTED VALUE FOR CONSENSUS: ',/rfc/@consensus,')')"/>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported value for /rfc/@consensus: ', /rfc/@consensus)"/>
+        <xsl:with-param name="inline" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- Header format as defined in RFC 5741, and deployed end of Dec 2009 -->
+<xsl:variable name="header-format">
+  <xsl:choose>
+    <xsl:when test="$pub-yearmonth >= 201001 or
+      ($rfcno=5741 or $rfcno=5742 or $rfcno=5743)"
+      >2010</xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="rfc-boilerplate">
+  <xsl:choose>
+    <!-- RFC boilerplate as defined in RFC 5741, and deployed end of Dec 2009 -->
+    <xsl:when test="$pub-yearmonth >= 201001 or
+      ($rfcno=5741 or $rfcno=5742 or $rfcno=5743)"
+      >2010</xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- the reference to the latest and greatest headers-and-boilerplates document -->
+<xsl:variable name="hab-reference">
+  <xsl:choose>
+    <xsl:when test="$pub-yearmonth >= 201606 or ($rfcno=7846 or $rfcno=7865 or $rfcno=7866 or $rfcno=7873 or $rfcno=7879 or $rfcno=7892)">Section 2 of RFC 7841</xsl:when>
+    <xsl:otherwise>Section 2 of RFC 5741</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="id-boilerplate">
+  <xsl:choose>
+    <!-- ID boilerplate approved by IESG on Jan 14 2010-->
+    <xsl:when test="$pub-yearmonth >= 201004"
+      >2010</xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="ipr-rfc4748" select="(
+  $ipr-rfc3667 and
+    ( $rfcno &gt;= 4715 and ( $rfcno != 4718 and $rfcno != 4735 and $rfcno != 4749 ))
+    or
+    ( $rfcno=4578 or $rfcno=4582 or $rfcno=4583 or $rfcno=4628 or $rfcno=4629 or $rfcno=4639 or $rfcno=4651 or $rfcno=4682 or $rfcno=4684 or $rfcno=4695 or $rfcno=4696 )
+    or
+    ( not(/rfc/@number) and $pub-yearmonth >= 200611)
+  )" />
+
+<xsl:variable name="ipr-2007-08" select="(
+  $ipr-rfc4748 and
+    (
+      ($rfcno &gt; 5000
+        and $rfcno != 5020
+        and $rfcno != 5021
+        and $rfcno != 5034
+        and $rfcno != 5052
+        and $rfcno != 5065
+        and $rfcno != 5094) or
+      ($xml2rfc-ext-pub-year >= 2008) or
+      (not(/rfc/@number) and $pub-yearmonth >= 200709)
+    )
+  )" />
+
+<xsl:variable name="ipr-2008-11" select="(
+    /rfc/@number and $pub-yearmonth >= 200811
+  )
+  or
+  (
+    /rfc/@ipr = 'trust200811' or
+    /rfc/@ipr = 'noModificationTrust200811' or
+    /rfc/@ipr = 'noDerivativesTrust200902' or
+    /rfc/@ipr = 'trust200902' or
+    /rfc/@ipr = 'noModificationTrust200902' or
+    /rfc/@ipr = 'noDerivativesTrust200902' or
+    /rfc/@ipr = 'pre5378Trust200902'
+  )" />
+
+<xsl:variable name="ipr-2009-02" select="(
+    $ipr-2008-11 and $pub-yearmonth >= 200902
+  )" />
+
+<!-- this makes the Sep 2009 TLP text depend on the publication date to be >= 2009-11
+     for IDs, and around 2009-09 for RFCs-->
+<xsl:variable name="ipr-2009-09" select="(
+    ( not(/rfc/@number) and $pub-yearmonth >= 200911 )
+    or
+    (
+      /rfc/@number and $pub-yearmonth >= 200909 and
+      $rfcno!=5582 and $rfcno!=5621 and $rfcno!=5632 and $rfcno!=5645 and $rfcno!=5646 and $rfcno!=5681
+    )
+  )" />
+
+<!-- this makes the Jan 2010 TLP text depend on the publication date to be >= 2010-04
+     for IDs, and around 2010-01 for RFCs-->
+<xsl:variable name="ipr-2010-01" select="(
+    ( not(/rfc/@number) and $pub-yearmonth >= 201004 )
+    or
+    (
+      /rfc/@number and ($pub-yearmonth >= 201001 or
+      $rfcno=5741 or $rfcno=5742 or $rfcno=5743)
+    )
+  )" />
+
+<!-- see http://mailman.rfc-editor.org/pipermail/rfc-interest/2009-June/001373.html -->
+<!-- for IDs, implement the change as 2009-11 -->
+<xsl:variable name="abstract-first" select="(
+    (/rfc/@number and $pub-yearmonth >= 200907)
+    or
+    (not(/rfc/@number) and $pub-yearmonth >= 200911)
+  )" />
+
+<!-- RFC 7322 changed the placement of notes -->
+<xsl:variable name="notes-follow-abstract" select="(
+    (/rfc/@number and /rfc/@number >= 7200)
+    or
+    ($pub-yearmonth >= 201409)
+  )" />
+
+<!-- funding switch -->
+<xsl:variable name="funding0" select="(
+  $rfcno &gt; 2499) or
+  (not(/rfc/@number) and /rfc/@docName and $xml2rfc-ext-pub-year &gt;= 1999
+  )" />
+
+<xsl:variable name="funding1" select="(
+  $rfcno &gt; 4320) or
+  (not(/rfc/@number) and /rfc/@docName and $xml2rfc-ext-pub-year &gt;= 2006
+  )" />
+
+<xsl:variable name="no-funding" select="$ipr-2007-08"/>
+
+<xsl:variable name="no-copylong" select="$ipr-2008-11"/>
+
+<!-- will document have an index -->
+<xsl:variable name="has-index" select="(//iref or (//xref and $xml2rfc-ext-include-references-in-index='yes')) and $xml2rfc-ext-include-index!='no'" />
+
+<!-- does the document contain edits? -->
+<xsl:variable name="has-edits" select="//ed:ins | //ed:del | //ed:replace" />
+
+<!-- does the document have a published-as-rfc link? -->
+<xsl:variable name="published-as-rfc" select="/*/x:link[@rel='Alternate' and starts-with(@title,'RFC')]"/>
+
+
+<xsl:template match="text()[not(ancestor::artwork) and not(ancestor::sourcecode)]">
+  <xsl:variable name="ws" select="'&#9;&#10;&#13;&#32;'"/>
+  <xsl:variable name="starts-with-ws" select="'' = translate(substring(.,1,1),$ws,'')"/>
+  <xsl:variable name="ends-with-ws" select="'' = translate(substring(.,string-length(.),1),$ws,'')"/>
+  <xsl:variable name="normalized" select="normalize-space(.)"/>
+  <!--<xsl:message> Orig: "<xsl:value-of select="."/>"</xsl:message>
+  <xsl:message>Start: "<xsl:value-of select="$starts-with-ws"/>"</xsl:message>
+  <xsl:message>  End: "<xsl:value-of select="$ends-with-ws"/>"</xsl:message> -->
+  <xsl:if test="$starts-with-ws and (preceding-sibling::node() | parent::ed:ins | parent::ed:del)">
+    <xsl:text> </xsl:text>
+  </xsl:if>
+  <xsl:value-of select="$normalized"/>
+  <xsl:if test="$ends-with-ws and $normalized!='' and (following-sibling::node() | parent::ed:ins | parent::ed:del)">
+    <xsl:text> </xsl:text>
+  </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="abstract">
+  <xsl:call-template name="check-no-text-content"/>
+  <section id="{$anchor-pref}abstract">
+    <xsl:call-template name="insert-errata">
+      <xsl:with-param name="section" select="'abstract'"/>
+    </xsl:call-template>
+    <h2><a href="#{$anchor-pref}abstract">Abstract</a></h2>
+    <xsl:apply-templates />
+  </section>
+</xsl:template>
+
+<msxsl:script language="JScript" implements-prefix="myns">
+  function parseXml(str) {
+    try {
+      var doc = new ActiveXObject("MSXML2.DOMDocument");
+      doc.async = false;
+      if (doc.loadXML(str)) {
+        return "";
+      }
+      else {
+        return doc.parseError.reason + "\n" + doc.parseError.srcText + " (" + doc.parseError.line + "/" + doc.parseError.linepos + ")";
+      }
+    }
+    catch(e) {
+      return "";
+    }
+  }
+</msxsl:script>
+
+<xsl:template name="add-artwork-class">
+  <xsl:variable name="v">
+    <xsl:choose>
+      <xsl:when test="@type='abnf' or @type='abnf2045' or @type='abnf2616' or @type='abnf7230' or @type='application/xml-dtd' or @type='inline' or @type='application/relax-ng-compact-syntax'">inline</xsl:when>
+      <xsl:when test="starts-with(@type,'message/http') and contains(@type,'msgtype=&quot;request&quot;')">text2</xsl:when>
+      <xsl:when test="starts-with(@type,'message/http')">text</xsl:when>
+      <xsl:when test="starts-with(@type,'drawing')">drawing</xsl:when>
+      <xsl:when test="starts-with(@type,'text/plain') or @type='example' or @type='code' or @type='application/xml-dtd' or @type='application/json'">text</xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+    <xsl:if test="@x:lang and $prettyprint-class!=''">
+      <xsl:value-of select="concat(' ',$prettyprint-class)"/>
+      <xsl:if test="@x:lang!=''">
+        <xsl:value-of select="concat(' lang-',@x:lang)"/>
+      </xsl:if>
+    </xsl:if>
+  </xsl:variable>
+  <xsl:if test="normalize-space($v)!=''">
+    <xsl:attribute name="class"><xsl:value-of select="normalize-space($v)"/></xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insert-begin-code">
+  <xsl:if test="@x:is-code-component='yes'">
+    <pre class="ccmarker cct"><span>&lt;CODE BEGINS></span></pre>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insert-end-code">
+  <xsl:if test="@x:is-code-component='yes'">
+    <pre class="ccmarker ccb"><span>&lt;CODE ENDS></span></pre>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="artwork|sourcecode">
+  <xsl:if test="not(ancestor::ed:del) and $xml2rfc-ext-parse-xml-in-artwork='yes' and function-available('myns:parseXml')" use-when="function-available('myns:parseXml')">
+    <xsl:if test="contains(.,'&lt;?xml')">
+      <xsl:variable name="body" select="substring-after(substring-after(.,'&lt;?xml'),'?>')" />
+      <xsl:if test="$body!='' and myns:parseXml($body)!=''">
+        <table style="background-color: red; border-width: thin; border-style: solid; border-color: black;">
+        <tr><td>
+        XML PARSE ERROR; parsed the body below:
+        <pre>
+        <xsl:value-of select="$body"/>
+        </pre>
+        resulting in:
+        <pre>
+        <xsl:value-of select="myns:parseXml($body)" />
+        </pre>
+        </td></tr></table>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="@ed:parse-xml-after">
+      <xsl:if test="myns:parseXml(string(.))!=''">
+        <table style="background-color: red; border-width: thin; border-style: solid; border-color: black;">
+        <tr><td>
+        XML PARSE ERROR:
+        <pre><xsl:value-of select="myns:parseXml(string(.))" /></pre>
+        </td></tr></table>
+      </xsl:if>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="contains(.,'&#9;')">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg" select="'artwork contains HTAB character'"/>
+      <xsl:with-param name="inline" select="'no'"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:variable name="display">
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-ext-allow-markup-in-artwork='yes'">
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="."/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="@align='right'">
+      <div style="display:table; margin-left: auto; margin-right: 0em;">
+        <xsl:call-template name="insert-begin-code"/>
+        <pre style="margin-left: 0em;">
+          <xsl:call-template name="add-artwork-class"/>
+          <xsl:call-template name="insertInsDelClass"/>
+          <xsl:copy-of select="$display"/>
+        </pre>
+        <xsl:call-template name="insert-end-code"/>
+      </div>
+    </xsl:when>
+    <xsl:when test="@align='center'">
+      <div style="display:table; margin-left: auto; margin-right: auto;">
+        <xsl:call-template name="insert-begin-code"/>
+        <pre style="margin-left: 0em;">
+          <xsl:call-template name="add-artwork-class"/>
+          <xsl:call-template name="insertInsDelClass"/>
+          <xsl:copy-of select="$display"/>
+        </pre>
+        <xsl:call-template name="insert-end-code"/>
+      </div>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="insert-begin-code"/>
+      <pre>
+        <xsl:call-template name="add-artwork-class"/>
+        <xsl:call-template name="insertInsDelClass"/>
+        <xsl:copy-of select="$display"/>
+      </pre>
+      <xsl:call-template name="insert-end-code"/>
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:call-template name="check-artwork-width">
+    <xsl:with-param name="content"><xsl:apply-templates/></xsl:with-param>
+    <xsl:with-param name="indent"><xsl:value-of select="string-length(@x:indent-with)"/></xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<!-- special case for first text node in artwork -->
+<xsl:template match="artwork/text()[1]">
+  <xsl:choose>
+    <xsl:when test="starts-with(.,'&#10;')">
+      <!-- reduce leading whitespace -->
+      <xsl:value-of select="substring(.,2)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="."/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="check-artwork-width">
+  <xsl:param name="content"/>
+  <xsl:param name="indent"/>
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-check-artwork-width='no'">
+      <!-- skip check -->
+    </xsl:when>
+    <xsl:when test="not(contains($content,'&#10;'))">
+      <xsl:if test="string-length($content) > 69 + number($indent)">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">artwork line too long: '<xsl:value-of select="$content"/>' (<xsl:value-of select="string-length($content)"/> characters)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="start" select="substring-before($content,'&#10;')"/>
+      <xsl:variable name="end" select="substring-after($content,'&#10;')"/>
+      <xsl:variable name="max">
+        <xsl:choose>
+          <xsl:when test="$indent!=''"><xsl:value-of select="69 + $indent"/></xsl:when>
+          <xsl:otherwise>69</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <xsl:if test="string-length($start) > $max">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">artwork line too long: '<xsl:value-of select="$start"/>' (<xsl:value-of select="string-length($start)"/> characters)</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="check-artwork-width">
+        <xsl:with-param name="content" select="$end"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="artwork[@src and starts-with(@type,'image/') or @type='svg']|artwork[svg:svg]">
+  <xsl:variable name="class">
+    <xsl:value-of select="$css-artwork"/>
+    <xsl:text> </xsl:text>
+    <xsl:if test="svg:svg">
+      <xsl:value-of select="$css-art-svg"/>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="@align='center'"><xsl:text> </xsl:text><xsl:value-of select="$css-center"/></xsl:when>
+      <xsl:when test="@align='right'"><xsl:text> </xsl:text><xsl:value-of select="$css-right"/></xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <div class="{normalize-space($class)}">
+    <xsl:choose>
+      <xsl:when test="svg:svg">
+        <xsl:copy-of select="svg:svg"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="alt">
+          <xsl:choose>
+            <xsl:when test="@alt!=''">
+              <xsl:value-of select="@alt"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="."/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <img src="{@src}">
+          <xsl:if test="normalize-space($alt)!=''">
+            <xsl:attribute name="alt"><xsl:value-of select="$alt"/></xsl:attribute>
+          </xsl:if>
+          <xsl:if test="@width and @width!=''">
+            <xsl:copy-of select="@width"/>
+          </xsl:if>
+          <xsl:if test="@height and @height!=''">
+            <xsl:copy-of select="@height"/>
+          </xsl:if>
+        </img>
+      </xsl:otherwise>
+    </xsl:choose>
+  </div>
+</xsl:template>
+
+<xsl:template match="author|x:contributor">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <address>
+    <xsl:call-template name="emit-author"/>
+
+    <xsl:if test="@asciiFullname!='' or organization/@ascii!='' or postal/*/@ascii">
+      <br/><br/>
+      <em>Additional contact information:</em>
+      <br/>
+      <xsl:call-template name="emit-author">
+        <xsl:with-param name="ascii" select="false()"/>
+      </xsl:call-template>
+    </xsl:if>
+  </address>
+</xsl:template>
+
+<xsl:template name="emit-author">
+  <xsl:param name="ascii" select="true()"/>
+  <b>
+    <xsl:choose>
+      <xsl:when test="@asciiFullname!='' and $ascii">
+        <xsl:value-of select="@asciiFullname" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@fullname" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </b>
+  <xsl:if test="@role">
+    <xsl:text> (</xsl:text>
+    <xsl:value-of select="@role" />
+    <xsl:text>)</xsl:text>
+  </xsl:if>
+  <!-- annotation support for Martin "uuml" Duerst -->
+  <xsl:if test="@x:annotation">
+    <xsl:text> </xsl:text>
+    <i><xsl:value-of select="@x:annotation"/></i>
+  </xsl:if>
+
+  <xsl:if test="normalize-space(concat(organization,organization/@ascii)) != ''">
+    <br/>
+    <xsl:choose>
+      <xsl:when test="organization/@ascii!='' and $ascii">
+        <xsl:value-of select="organization/@ascii" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="organization" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+
+  <xsl:if test="address/postal">
+    <xsl:for-each select="address/postal/street">
+      <xsl:variable name="street">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="name" select="'street'"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:if test="$street!=''">
+        <br/>
+        <xsl:value-of select="$street"/>
+      </xsl:if>
+    </xsl:for-each>
+    <xsl:for-each select="address/postal/postalLine">
+      <xsl:variable name="line">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="name" select="'postalLine'"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:if test="$line!=''">
+        <br/>
+        <xsl:value-of select="$line"/>
+      </xsl:if>
+    </xsl:for-each>
+    <xsl:if test="address/postal/city|address/postal/region|address/postal/code">
+      <br/>
+      <xsl:variable name="city">
+        <xsl:if test="address/postal/city">
+          <xsl:call-template name="extract-normalized">
+            <xsl:with-param name="node" select="address/postal/city"/>
+            <xsl:with-param name="name" select="'address/postal/city'"/>
+            <xsl:with-param name="ascii" select="$ascii"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:variable>
+      <xsl:variable name="region">
+        <xsl:if test="address/postal/region">
+          <xsl:call-template name="extract-normalized">
+            <xsl:with-param name="node" select="address/postal/region"/>
+            <xsl:with-param name="name" select="'address/postal/region'"/>
+            <xsl:with-param name="ascii" select="$ascii"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:variable>
+      <xsl:variable name="code">
+        <xsl:if test="address/postal/code">
+          <xsl:call-template name="extract-normalized">
+            <xsl:with-param name="node" select="address/postal/code"/>
+            <xsl:with-param name="name" select="'address/postal/code'"/>
+            <xsl:with-param name="ascii" select="$ascii"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:variable>
+
+      <xsl:if test="$city!=''">
+        <xsl:value-of select="$city"/>
+      </xsl:if>
+
+      <xsl:variable name="region-and-code">
+        <xsl:value-of select="$region"/>
+        <xsl:if test="$region!='' and $code!=''">
+          <xsl:text>&#160;</xsl:text>
+        </xsl:if>
+        <xsl:value-of select="$code"/>
+      </xsl:variable>
+      
+      <xsl:if test="$region-and-code!=''">
+        <xsl:if test="$city!=''">
+          <xsl:text>, </xsl:text>
+        </xsl:if>
+        <xsl:value-of select="$region-and-code"/>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="address/postal/country">
+      <xsl:variable name="country">
+        <xsl:call-template name="extract-normalized">
+          <xsl:with-param name="node" select="address/postal/country"/>
+          <xsl:with-param name="name" select="'address/postal/country'"/>
+          <xsl:with-param name="ascii" select="$ascii"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:if test="$country!=''">
+        <br/>
+        <xsl:value-of select="$country"/>
+      </xsl:if>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="address/phone">
+    <xsl:variable name="phone">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="address/phone"/>
+        <xsl:with-param name="name" select="'address/phone'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:if test="$phone!=''">
+      <br/>
+      <xsl:text>Phone: </xsl:text>
+      <a href="tel:{translate($phone,' ','')}"><xsl:value-of select="$phone" /></a>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="address/facsimile">
+    <xsl:variable name="facsimile">
+      <xsl:call-template name="extract-normalized">
+        <xsl:with-param name="node" select="address/facsimile"/>
+        <xsl:with-param name="name" select="'address/facsimile'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:if test="$facsimile!=''">
+      <br/>
+      <xsl:text>Fax: </xsl:text>
+      <a href="fax:{translate($facsimile,' ','')}"><xsl:value-of select="$facsimile" /></a>
+    </xsl:if>
+  </xsl:if>
+  <xsl:for-each select="address/email">
+    <xsl:variable name="email">
+      <xsl:call-template name="extract-email"/>
+    </xsl:variable>
+
+    <br/>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-rfcedstyle='yes'">Email: </xsl:when>
+      <xsl:otherwise>EMail: </xsl:otherwise>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-linkmailto!='no'">
+        <a href="mailto:{$email}"><xsl:value-of select="$email" /></a>   
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$email" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+  <xsl:for-each select="address/uri">
+    <xsl:variable name="uri">
+      <xsl:call-template name="extract-uri"/>
+    </xsl:variable>
+    <xsl:if test="$uri!=''">
+      <br/>
+      <xsl:text>URI: </xsl:text>
+      <a href="{$uri}"><xsl:value-of select="$uri" /></a>
+      <xsl:if test="@x:annotation">
+        <xsl:text> </xsl:text>
+        <i><xsl:value-of select="@x:annotation"/></i>
+      </xsl:if>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- this is a named template because <back> may be absent -->
+<xsl:template name="back">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <!-- add editorial comments -->
+  <xsl:if test="//cref and $xml2rfc-comments='yes' and $xml2rfc-inline!='yes'">
+    <xsl:call-template name="insertComments" />
+  </xsl:if>
+
+  <!-- next, add information about the document's authors -->
+  <xsl:if test="$xml2rfc-ext-authors-section='before-appendices'">
+    <xsl:call-template name="insertAuthors" />
+  </xsl:if>
+
+  <!-- add all other top-level sections under <back> -->
+  <xsl:apply-templates select="back/*[not(self::references) and not(self::ed:replace and .//references)]" />
+
+  <!-- insert the index if index entries exist -->
+  <!-- note it always comes before the authors section -->
+  <xsl:if test="$has-index">
+    <xsl:call-template name="insertIndex" />
+  </xsl:if>
+
+  <!-- Authors section is the absolute last thing, except for copyright stuff -->
+  <xsl:if test="$xml2rfc-ext-authors-section='end'">
+    <xsl:call-template name="insertAuthors" />
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-private=''">
+    <!-- copyright statements -->
+    <xsl:variable name="copyright">
+      <xsl:call-template name="insertCopyright" />
+    </xsl:variable>
+
+    <!-- emit it -->
+    <xsl:choose>
+      <xsl:when test="function-available('exslt:node-set')">
+        <xsl:apply-templates select="exslt:node-set($copyright)" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="$node-set-warning"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template match="eref[node()]">
+  <a href="{@target}"><xsl:apply-templates /></a>
+</xsl:template>
+
+<xsl:template match="eref[not(node())]">
+  <xsl:text>&lt;</xsl:text>
+  <a href="{@target}"><xsl:value-of select="@target" /></a>
+  <xsl:text>&gt;</xsl:text>
+</xsl:template>
+
+<xsl:template match="figure">
+  <xsl:call-template name="check-no-text-content"/>
+  <!-- warn about the attributes that we do not support -->
+  <xsl:for-each select="@*[local-name()!='title' and local-name()!='suppress-title' and local-name()!='anchor' and local-name()!='pn' and normalize-space(.)!='']">
+    <xsl:if test="local-name(.)!='align' or normalize-space(.)!='left'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('unsupported attribute ',local-name(.),' on figure element')"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:for-each>
+  <xsl:variable name="anch-container">
+    <xsl:choose>
+      <xsl:when test="ancestor::t">span</xsl:when>
+      <xsl:otherwise>div</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:if test="@anchor!=''">
+    <xsl:call-template name="check-anchor"/>
+    <xsl:element name="{$anch-container}">
+      <xsl:attribute name="id"><xsl:value-of select="@anchor"/></xsl:attribute>
+    </xsl:element>
+  </xsl:if>
+  <xsl:variable name="anch">
+    <xsl:call-template name="get-figure-anchor"/>
+  </xsl:variable>
+  <xsl:element name="{$anch-container}">
+    <xsl:attribute name="id"><xsl:value-of select="$anch"/></xsl:attribute>
+    <xsl:apply-templates select="*[not(self::name)]"/>
+  </xsl:element>
+  <xsl:if test="(@title!='' or name) or (@anchor!='' and not(@suppress-title='true'))">
+    <xsl:variable name="n"><xsl:call-template name="get-figure-number"/></xsl:variable>
+    <p class="figure">
+      <xsl:if test="not(starts-with($n,'u'))">
+        <xsl:text>Figure </xsl:text>
+        <xsl:value-of select="$n"/>
+        <xsl:if test="@title!='' or name">: </xsl:if>
+      </xsl:if>
+      <xsl:call-template name="insertTitle"/>
+    </p>
+  </xsl:if>
+</xsl:template>
+
+<xsl:variable name="all-notes" select="/rfc/front/note"/>
+<xsl:variable name="all-edited-notes" select="/rfc/front/ed:replace[.//note]"/>
+<xsl:variable name="notes-not-in-boilerplate" select="$all-notes[@title!='IESG Note' or $xml2rfc-private!='' or $notes-follow-abstract]"/>
+<xsl:variable name="edited-notes-not-in-boilerplate" select="$all-edited-notes[.//note/@title!='IESG Note' or $xml2rfc-private!='' or $notes-follow-abstract]"/>
+<xsl:variable name="notes-in-boilerplate" select="$all-notes[not(@title!='IESG Note' or $xml2rfc-private!='' or $notes-follow-abstract)]"/>
+<xsl:variable name="edited-notes-in-boilerplate" select="$all-edited-notes[not(.//note/@title!='IESG Note' or $xml2rfc-private!='' or $notes-follow-abstract)]"/>
+
+<xsl:template match="front">
+  <xsl:call-template name="check-no-text-content"/>
+  <header>
+    <xsl:if test="$xml2rfc-topblock!='no'">
+      <!-- collect information for left column -->
+      <xsl:variable name="leftColumn">
+        <xsl:call-template name="collectLeftHeaderColumn" />
+      </xsl:variable>
+      <!-- collect information for right column -->
+      <xsl:variable name="rightColumn">
+        <xsl:call-template name="collectRightHeaderColumn" />
+      </xsl:variable>
+      <!-- insert the collected information -->
+      <table class="{$css-header}" id="{$anchor-pref}headerblock">
+        <xsl:choose>
+          <xsl:when test="function-available('exslt:node-set')">
+            <xsl:call-template name="emitheader">
+              <xsl:with-param name="lc" select="exslt:node-set($leftColumn)" />
+              <xsl:with-param name="rc" select="exslt:node-set($rightColumn)" />
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="$node-set-warning"/>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </table>
+    </xsl:if>
+
+    <div id="{$anchor-pref}title">
+      <!-- main title -->
+      <h1><xsl:apply-templates select="title"/></h1>
+      <xsl:if test="/rfc/@docName">
+        <xsl:variable name="docname" select="/rfc/@docName"/>
+        <xsl:choose>
+          <xsl:when test="$rfcno!=''">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' is ignored because an RFC number is specified as well.</xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <div class="filename"><xsl:value-of select="$docname"/></div>
+          </xsl:otherwise>
+        </xsl:choose>
+        
+        <xsl:variable name="docname-noext">
+          <xsl:choose>
+            <xsl:when test="contains($docname,'.')">
+              <xsl:call-template name="warning">
+                <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' should contain the base name, not the filename (thus no file extension).</xsl:with-param>
+              </xsl:call-template>
+              <xsl:value-of select="substring-before($docname,'.')"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$docname"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+  
+        <!-- more name checks -->
+        <xsl:variable name="offending" select="translate($docname,concat($lcase,$digits,'-.'),'')"/>
+        <xsl:if test="$offending != ''">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' should not contain the character '<xsl:value-of select="substring($offending,1,1)"/>'.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+  
+        <xsl:if test="contains($docname,'--')">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' should not contain the character sequence '--'.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+  
+        <xsl:if test="not(starts-with($docname,'draft-'))">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' should start with 'draft-'.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+  
+        <!-- sequence number -->
+        <xsl:variable name="seq">
+          <xsl:choose>
+            <xsl:when test="substring($docname-noext,string-length($docname-noext) + 1 - string-length('-latest'))='-latest'">latest</xsl:when>
+            <xsl:when test="substring($docname-noext,string-length($docname-noext) - 2, 1)='-'"><xsl:value-of select="substring($docname-noext,string-length($docname-noext)-1)"/></xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </xsl:variable>
+  
+        <xsl:if test="$seq='' or ($seq!='latest' and translate($seq,$digits,'')!='')">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>' should end with a two-digit sequence number or 'latest'.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+  
+        <xsl:if test="string-length($docname)-string-length($seq) > 50">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">The @docName attribute '<xsl:value-of select="$docname"/>', excluding sequence number, should have less than 50 characters.</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+  
+      </xsl:if>
+    </div>
+  </header>
+
+  <!-- insert notice about update -->
+  <xsl:if test="$published-as-rfc">
+    <p class="{$css-publishedasrfc}">
+      <b>Note:</b> a later version of this document has been published as <a href="{$published-as-rfc/@href}"><xsl:value-of select="$published-as-rfc/@title"/></a>.
+    </p>
+  </xsl:if>
+
+  <!-- check for conforming ipr attribute -->
+  <xsl:choose>
+    <xsl:when test="not(/rfc/@ipr)">
+      <xsl:if test="not(/rfc/@number) and $xml2rfc-private=''">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Either /rfc/@ipr or /rfc/@number is required</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="/rfc/@ipr = 'full2026'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativeWorks'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativeWorksNow'" />
+    <xsl:when test="/rfc/@ipr = 'none'" />
+    <xsl:when test="/rfc/@ipr = 'full3667'" />
+    <xsl:when test="/rfc/@ipr = 'noModification3667'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivatives3667'" />
+    <xsl:when test="/rfc/@ipr = 'full3978'" />
+    <xsl:when test="/rfc/@ipr = 'noModification3978'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivatives3978'" />
+    <xsl:when test="/rfc/@ipr = 'trust200811'" />
+    <xsl:when test="/rfc/@ipr = 'noModificationTrust200811'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativesTrust200811'" />
+    <xsl:when test="/rfc/@ipr = 'trust200902'" />
+    <xsl:when test="/rfc/@ipr = 'noModificationTrust200902'" />
+    <xsl:when test="/rfc/@ipr = 'noDerivativesTrust200902'" />
+    <xsl:when test="/rfc/@ipr = 'pre5378Trust200902'" />
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unknown value for /rfc/@ipr: ', /rfc/@ipr)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <xsl:call-template name="insert-errata">
+    <xsl:with-param name="section" select="'boilerplate'"/>
+  </xsl:call-template>
+
+  <xsl:if test="not($abstract-first)">
+    <xsl:if test="$xml2rfc-private=''">
+      <xsl:call-template name="emit-ietf-preamble">
+        <xsl:with-param name="notes" select="$notes-in-boilerplate|$edited-notes-in-boilerplate"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+  
+  <xsl:apply-templates select="abstract" />
+  <xsl:if test="$notes-follow-abstract">
+    <xsl:apply-templates select="$notes-not-in-boilerplate|$edited-notes-not-in-boilerplate" />
+  </xsl:if>
+
+  <xsl:if test="$abstract-first">
+    <xsl:if test="$xml2rfc-private=''">
+      <xsl:call-template name="emit-ietf-preamble">
+        <xsl:with-param name="notes" select="$notes-in-boilerplate|$edited-notes-in-boilerplate"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <xsl:if test="not($notes-follow-abstract)">
+    <xsl:apply-templates select="$notes-not-in-boilerplate|$edited-notes-not-in-boilerplate" />
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-toc='yes'">
+    <xsl:apply-templates select="/" mode="toc" />
+    <xsl:call-template name="insertTocAppendix" />
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="emit-ietf-preamble">
+  <xsl:param name="notes"/>
+
+  <!-- Get status info formatted as per RFC2629-->
+  <xsl:variable name="preamble">
+    <xsl:for-each select="/rfc">
+      <xsl:call-template name="insertPreamble">
+        <xsl:with-param name="notes" select="$notes"/>
+      </xsl:call-template>
+    </xsl:for-each>
+  </xsl:variable>
+
+  <!-- get document-supplied boilerplate -->
+  <xsl:variable name="userboiler" select="/rfc/front/boilerplate"/>
+
+  <!-- emit it -->
+  <xsl:choose>
+    <xsl:when test="function-available('exslt:node-set')">
+      <xsl:variable name="differ" select="$userboiler and translate(normalize-space(string($userboiler)),' ','')!=translate(normalize-space(string($preamble)),' ','')"/>
+      <!--<xsl:if test="$differ">
+        <xsl:message>1: <xsl:value-of select="normalize-space(string($userboiler))"/></xsl:message>
+        <xsl:message>2: <xsl:value-of select="normalize-space(string($preamble))"/></xsl:message>
+      </xsl:if>-->
+      <xsl:apply-templates select="exslt:node-set($preamble)" />
+      <xsl:if test="$differ">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="'user-supplied boilerplate differs from auto-generated boilerplate (inserting auto-generated)'"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="$node-set-warning"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="iref">
+  <xsl:variable name="anchor"><xsl:call-template name="compute-iref-anchor"/></xsl:variable>
+  <xsl:choose>
+    <xsl:when test="parent::figure">
+      <div id="{$anchor}"/>
+    </xsl:when>
+    <xsl:when test="ancestor::t or ancestor::artwork or ancestor::preamble or ancestor::postamble">
+      <span id="{$anchor}"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <div id="{$anchor}"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="compute-iref-anchor">
+  <xsl:variable name="first" select="translate(substring(@item,1,1),$ucase,$lcase)"/>
+  <xsl:variable name="nkey" select="translate($first,$alnum,'')"/>
+  <xsl:choose>
+    <xsl:when test="count(.|$section-level-irefs)=count($section-level-irefs)">
+      <xsl:for-each select="..">
+        <xsl:value-of select="$anchor-pref"/>section.<xsl:call-template name="get-section-number"/>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="$nkey=''">
+      <xsl:value-of select="$anchor-pref"/>iref.<xsl:value-of select="$first"/>.<xsl:number level="any" count="iref[starts-with(translate(@item,$ucase,$lcase),$first) and count(.|$section-level-irefs)!=count($section-level-irefs)]"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$anchor-pref"/>iref.<xsl:number level="any" count="iref[translate(substring(@item,1,1),$alnum,'')!='' and count(.|$section-level-irefs)!=count($section-level-irefs)]"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="compute-extref-anchor">
+  <xsl:variable name="first" select="translate(substring(.,1,1),$ucase,$lcase)"/>
+  <xsl:variable name="nkey" select="translate($first,$lcase,'')"/>
+  <xsl:choose>
+    <xsl:when test="$nkey=''">
+      <xsl:value-of select="$anchor-pref"/>extref.<xsl:value-of select="$first"/>.<xsl:number level="any" count="x:ref[starts-with(translate(.,$ucase,$lcase),$first)]"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$anchor-pref"/>extref.<xsl:number level="any" count="x:ref[translate(substring(.,1,1),concat($lcase,$ucase),'')='']"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- list templates depend on the list style -->
+
+<xsl:template match="list[@style='empty' or (not(@style) and not(ancestor::list[@style]) or (not(@style) and ancestor::list[@style='empty']))]">
+  <xsl:call-template name="check-no-text-content"/>
+  <ul class="empty">
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ul>
+</xsl:template>
+
+<xsl:template match="ol[string-length(@type)>1]">
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="@group">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="@start">
+        <xsl:value-of select="@start"/>
+      </xsl:when>
+      <xsl:otherwise>1</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$p!='' and not(ancestor::list) and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <div id="{$anchor-pref}section.{$p}">
+        <dl>
+          <xsl:call-template name="copy-anchor"/>
+          <xsl:for-each select="li">
+            <xsl:variable name="label">
+              <xsl:call-template name="expand-format-percent">
+                <xsl:with-param name="format" select="../@type"/>
+                <xsl:with-param name="pos" select="$start - 1 + position()"/>
+              </xsl:call-template>
+            </xsl:variable>
+            <dt>
+              <xsl:call-template name="copy-anchor"/>
+              <xsl:value-of select="$label"/>
+            </dt>
+            <dd>
+              <xsl:apply-templates/>
+            </dd>
+          </xsl:for-each>
+        </dl>
+      </div>
+    </xsl:when>
+    <xsl:otherwise>
+      <dl>
+        <xsl:call-template name="copy-anchor"/>
+        <xsl:for-each select="li">
+          <xsl:variable name="label">
+            <xsl:call-template name="expand-format-percent">
+              <xsl:with-param name="format" select="../@type"/>
+              <xsl:with-param name="pos" select="$start - 1 + position()"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <dt>
+            <xsl:call-template name="copy-anchor"/>
+            <xsl:value-of select="$label"/>
+          </dt>
+          <dd>
+            <xsl:apply-templates/>
+          </dd>
+        </xsl:for-each>
+      </dl>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="dl">
+  <xsl:variable name="hang" select="@hanging"/>
+  <xsl:variable name="spac" select="@spacing"/>
+  <xsl:variable name="class">
+    <xsl:if test="$spac='compact'">compact </xsl:if>
+    <xsl:if test="$hang='false'">nohang </xsl:if>
+  </xsl:variable>
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+  <div>
+    <xsl:if test="$p!='' and not(ancestor::list) and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,'section.',$p)"/></xsl:attribute>
+    </xsl:if>
+    <dl>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:if test="normalize-space($class)!=''">
+        <xsl:attribute name="class"><xsl:value-of select="normalize-space($class)"/></xsl:attribute>
+      </xsl:if>
+      <xsl:for-each select="dt">
+        <xsl:apply-templates select="."/>
+        <xsl:apply-templates select="following-sibling::dd[1]"/>
+      </xsl:for-each>
+    </dl>
+  </div>
+</xsl:template>
+
+<xsl:template match="dt">
+  <dt>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </dt>
+</xsl:template>
+
+<xsl:template match="dd">
+  <dd>
+    <xsl:variable name="block-level-children" select="t | dl"/>
+    <xsl:choose>
+      <xsl:when test="$block-level-children">
+        <!-- TODO: improve error handling-->
+        <xsl:for-each select="$block-level-children">
+          <xsl:choose>
+            <xsl:when test="self::t">
+              <p>
+                <xsl:call-template name="copy-anchor"/>
+                <xsl:apply-templates/>
+              </p>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:apply-templates select="."/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </dd>
+</xsl:template>
+
+<xsl:template match="list[starts-with(@style,'format ')]">
+  <xsl:call-template name="check-no-text-content"/>
+  <dl>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </dl>
+</xsl:template>
+
+<!-- get value of "compact" mode, checking subcompact first, then compact -->
+<xsl:template name="get-compact-setting">
+  <xsl:variable name="t1">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="preceding::processing-instruction('rfc')"/>
+      <xsl:with-param name="attr" select="'subcompact'"/>
+      <xsl:with-param name="default" select="'?'"/>
+      <xsl:with-param name="duplicate-warning" select="'no'"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$t1='?'">
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="preceding::processing-instruction('rfc')"/>
+        <xsl:with-param name="attr" select="'compact'"/>
+        <xsl:with-param name="default" select="'?'"/>
+        <xsl:with-param name="duplicate-warning" select="'no'"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$t1"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="list[@style='hanging']">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:variable name="compact">
+    <xsl:call-template name="get-compact-setting"/>
+  </xsl:variable>
+  <!-- insert a hard space for nested lists so that indentation works ok -->
+  <xsl:if test="ancestor::list and normalize-space(preceding-sibling::text())=''">
+    <xsl:text>&#160;</xsl:text>
+  </xsl:if>
+  <dl>
+    <xsl:if test="$compact='yes'">
+      <xsl:attribute name="class">compact</xsl:attribute>
+    </xsl:if>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </dl>
+</xsl:template>
+
+<xsl:template match="list[@style='numbers' or (not(@style) and ancestor::list[@style='numbers'])]">
+  <xsl:call-template name="check-no-text-content"/>
+  <ol>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ol>
+</xsl:template>
+
+<xsl:template name="ol-start">
+  <xsl:param name="node"/>
+  <xsl:variable name="group" select="$node/@group"/>
+  <xsl:variable name="prec" select="$node/preceding::ol[@group=$group]"/>
+  <xsl:choose>
+    <xsl:when test="$node/@start">
+      <xsl:value-of select="$node/@start"/>
+    </xsl:when>
+    <xsl:when test="$prec">
+      <xsl:variable name="s">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="$prec[last()]"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:value-of select="$s + count($prec[last()]/li)"/>
+    </xsl:when>
+    <xsl:otherwise>1</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="ol[not(@type) or string-length(@type)=1]">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="@group">
+        <xsl:call-template name="ol-start">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="@start">
+        <xsl:value-of select="@start"/>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+  <div>
+    <xsl:if test="$p!='' and not(ancestor::list) and not(ancestor::ul) and not(ancestor::dl) and not(ancestor::ol) and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,'section.',$p)"/></xsl:attribute>
+    </xsl:if>
+    <ol>
+      <xsl:if test="$start!=''">
+        <xsl:attribute name="start"><xsl:value-of select="$start"/></xsl:attribute>
+      </xsl:if>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:copy-of select="@type"/>
+      <xsl:apply-templates />
+    </ol>
+  </div> 
+</xsl:template>
+
+<xsl:template match="ul">
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+  <div>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:if test="$p!='' and not(ancestor::list) and not(ancestor::ul) and not(ancestor::dl) and not(ancestor::ol) and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,'section.',$p)"/></xsl:attribute>
+    </xsl:if>
+    <ul>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:if test="@empty='true'">
+        <xsl:attribute name="class">empty</xsl:attribute>
+      </xsl:if>
+      <xsl:apply-templates />
+    </ul>
+  </div>
+</xsl:template>
+
+<xsl:template match="li">
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+    <xsl:if test="not(following-sibling::li)">
+      <xsl:variable name="l">
+        <xsl:for-each select="..">
+          <xsl:call-template name="get-paragraph-number"/>
+        </xsl:for-each>
+      </xsl:variable>
+      <xsl:if test="$l!=''">
+        <a class='self' href='#{$anchor-pref}section.{$l}'>&#xb6;</a>
+      </xsl:if>
+    </xsl:if>
+  </li>
+</xsl:template>
+
+<xsl:template match="list[@style='letters' or (not(@style) and ancestor::list[@style='letters'])]">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:variable name="style">
+    <xsl:choose>
+      <!-- lowercase for even-numbered nesting levels -->
+      <xsl:when test="0=(count(ancestor::list[@style='letters']) mod 2)">la</xsl:when>
+      <!-- uppercase otherwise -->
+      <xsl:otherwise>ua</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <ol class="{$style}">
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ol>
+</xsl:template>
+
+<xsl:template match="list[@style='symbols' or (not(@style) and ancestor::list[@style='symbols'])]">
+  <xsl:call-template name="check-no-text-content"/>
+  <ul>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </ul>
+</xsl:template>
+
+
+<!-- same for t(ext) elements -->
+
+<xsl:template match="list[@style='empty' or not(@style)]/t | list[@style='empty' or not(@style)]/ed:replace/ed:*/t">
+  <xsl:if test="@hangText">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg" select="'t/@hangText used on unstyled list'"/>
+    </xsl:call-template>
+  </xsl:if>
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:apply-templates />
+  </li>
+</xsl:template>
+
+<xsl:template match="list[@style='numbers' or @style='symbols' or @style='letters']/x:lt">
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="t" />
+  </li>
+</xsl:template>
+
+<xsl:template match="list[@style='numbers' or @style='symbols' or @style='letters']/t | list[@style='numbers' or @style='symbols' or @style='letters']/ed:replace/ed:*/t">
+  <xsl:if test="@hangText">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg" select="'t/@hangText used on non-hanging list'"/>
+    </xsl:call-template>
+  </xsl:if>
+  <li>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:for-each select="../..">
+      <xsl:call-template name="insert-issue-pointer"/>
+    </xsl:for-each>
+    <xsl:apply-templates />
+  </li>
+</xsl:template>
+
+<xsl:template match="list[@style='hanging']/x:lt">
+  <xsl:if test="@hangText!=''">
+    <dt>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:variable name="del-node" select="ancestor::ed:del"/>
+      <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+      <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+      <xsl:for-each select="../..">
+        <xsl:call-template name="insert-issue-pointer">
+          <xsl:with-param name="deleted-anchor" select="$deleted"/>
+        </xsl:call-template>
+      </xsl:for-each>
+      <xsl:value-of select="@hangText" />
+    </dt>
+  </xsl:if>
+  <dd>
+    <xsl:call-template name="insertInsDelClass"/>
+    <!-- if hangIndent present, use 0.7 of the specified value (1em is the width of the "m" character -->
+    <xsl:if test="../@hangIndent">
+      <xsl:attribute name="style">margin-left: <xsl:value-of select="format-number(../@hangIndent * 0.7,'#.#')"/>em</xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates select="t" />
+  </dd>
+</xsl:template>
+
+<xsl:template match="list[@style='hanging']/t | list[@style='hanging']/ed:replace/ed:*/t">
+  <xsl:if test="@hangText!=''">
+    <dt>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:if test="count(preceding-sibling::t)=0">
+        <xsl:variable name="del-node" select="ancestor::ed:del"/>
+        <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+        <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+        <xsl:for-each select="../..">
+          <xsl:call-template name="insert-issue-pointer">
+            <xsl:with-param name="deleted-anchor" select="$deleted"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:if>
+      <xsl:value-of select="@hangText" />
+    </dt>
+  </xsl:if>
+
+  <xsl:variable name="dd-content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$dd-content!=''">
+      <dd>
+        <xsl:call-template name="insertInsDelClass"/>
+        <!-- if hangIndent present, use 0.7 of the specified value (1em is the width of the "m" character -->
+        <xsl:if test="../@hangIndent">
+          <xsl:attribute name="style">margin-left: <xsl:value-of select="format-number(../@hangIndent * 0.7,'#.#')"/>em</xsl:attribute>
+        </xsl:if>
+        <xsl:apply-templates />
+      </dd>
+    </xsl:when>
+    <xsl:otherwise>
+      <dd>&#160;</dd>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="list[starts-with(@style,'format ')]/t">
+  <xsl:variable name="list" select=".." />
+  <xsl:variable name="format" select="substring-after(../@style,'format ')" />
+  <xsl:variable name="pos">
+    <xsl:choose>
+      <xsl:when test="$list/@counter">
+        <xsl:number level="any" count="list[@counter=$list/@counter or (not(@counter) and @style=concat('format ',$list/@counter))]/t" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:number level="any" count="list[concat('format ',@counter)=$list/@style or (not(@counter) and @style=$list/@style)]/t" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <dt>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="expand-format-percent">
+      <xsl:with-param name="format" select="$format"/>
+      <xsl:with-param name="pos" select="$pos"/>
+    </xsl:call-template>
+  </dt>
+  <dd>
+    <xsl:apply-templates />
+  </dd>
+</xsl:template>
+
+<xsl:template name="expand-format-percent">
+  <xsl:param name="format"/>
+  <xsl:param name="pos"/>
+  
+  <xsl:choose>
+    <xsl:when test="$format=''"><!-- done--></xsl:when>
+    <xsl:when test="substring($format,1,1)!='%' or string-length($format)=1">
+      <xsl:value-of select="substring($format,1,1)"/>
+      <xsl:call-template name="expand-format-percent">
+        <xsl:with-param name="format" select="substring($format,2)"/>
+        <xsl:with-param name="pos" select="$pos"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="f" select="substring($format,2,1)"/>
+      <xsl:choose>
+        <xsl:when test="$f='%'">%</xsl:when>
+        <xsl:when test="$f='c'"><xsl:number value="$pos" format="a"/></xsl:when>
+        <xsl:when test="$f='C'"><xsl:number value="$pos" format="A"/></xsl:when>
+        <xsl:when test="$f='d'"><xsl:number value="$pos"/></xsl:when>
+        <xsl:when test="$f='i'"><xsl:number value="$pos" format="i"/></xsl:when>
+        <xsl:when test="$f='I'"><xsl:number value="$pos" format="I"/></xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('Unsupported % format: ', $f)"/>
+            <xsl:with-param name="inline" select="'no'"/>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:call-template name="expand-format-percent">
+        <xsl:with-param name="format" select="substring($format,3)"/>
+        <xsl:with-param name="pos" select="$pos"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+  
+</xsl:template>
+
+<xsl:template match="middle">
+  <xsl:apply-templates />
+  <xsl:apply-templates select="../back//references"/>
+</xsl:template>
+
+<xsl:template match="note">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:variable name="classes">
+    <xsl:text>note</xsl:text>
+    <xsl:text> </xsl:text>
+    <xsl:if test="@removeInRFC='true'">rfcEditorRemove</xsl:if>
+  </xsl:variable>
+  <xsl:variable name="num"><xsl:number/></xsl:variable>
+  <section id="{$anchor-pref}note.{$num}" class="{normalize-space($classes)}">
+    <h2>
+      <xsl:call-template name="insertInsDelClass"/>
+      <a href="#{$anchor-pref}note.{$num}">
+        <xsl:call-template name="insertTitle" />
+      </a>
+    </h2>
+    <xsl:if test="@removeInRFC='true' and t[1]!=$note-removeInRFC">
+      <xsl:variable name="t">
+        <t><xsl:value-of select="$note-removeInRFC"/></t>
+      </xsl:variable>
+      <xsl:variable name="link" select="concat($anchor-pref,'note.',$num,'.p.1')"/>
+      <div id="{$link}">
+        <xsl:apply-templates mode="t-content" select="exslt:node-set($t)//text()">
+          <xsl:with-param name="inherited-self-link" select="$link"/>
+        </xsl:apply-templates>
+      </div>
+    </xsl:if>
+    <xsl:apply-templates />
+  </section>
+</xsl:template>
+
+<xsl:template match="postamble">
+  <xsl:if test="normalize-space(.) != ''">
+    <p>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:call-template name="editingMark" />
+      <xsl:apply-templates />
+    </p>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="preamble">
+  <xsl:if test="normalize-space(.) != ''">
+    <p>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:call-template name="insertInsDelClass"/>
+      <xsl:call-template name="editingMark" />
+      <xsl:apply-templates />
+    </p>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="computed-auto-target">
+  <xsl:param name="bib"/>
+  <xsl:param name="ref"/>
+
+  <xsl:variable name="sec">
+    <xsl:choose>
+      <xsl:when test="$ref and starts-with($ref/@x:rel,'#') and not($ref/@x:sec) and not($ref/@section)">
+        <xsl:variable name="extdoc" select="document($bib/x:source/@href)"/>
+        <xsl:for-each select="$extdoc//*[@anchor=substring-after($ref/@x:rel,'#')]">
+          <xsl:call-template name="get-section-number"/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:when test="$ref and $ref/@section">
+        <xsl:value-of select="$ref/@section"/>
+      </xsl:when>
+      <xsl:when test="$ref">
+        <xsl:value-of select="$ref/@x:sec"/>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$ref and $bib/x:source/@href and $bib/x:source/@basename and $ref/@x:rel">
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension,$ref/@x:rel)" />
+    </xsl:when>
+    <xsl:when test="$ref and $bib/x:source/@href and $bib/x:source/@basename and $ref/@anchor">
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension,'#',$ref/@anchor)" />
+    </xsl:when>
+    <!-- tools.ietf.org won't have the "-latest" draft -->
+    <xsl:when test="$bib//seriesInfo/@name='Internet-Draft' and $bib/x:source/@href and $bib/x:source/@basename and substring($bib/x:source/@basename, (string-length($bib/x:source/@basename) - string-length('-latest')) + 1)='-latest'">
+      <xsl:value-of select="concat($bib/x:source/@basename,'.',$outputExtension)" />
+    </xsl:when>
+    <!-- TODO: this should handle the case where there's one BCP entry but
+    multiple RFC entries in a more useful way-->
+    <xsl:when test="$bib//seriesInfo/@name='RFC'">
+      <xsl:variable name="rfcEntries" select="$bib//seriesInfo[@name='RFC']"/>
+      <xsl:if test="count($rfcEntries)!=1">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('seriesInfo/@name=RFC encountered multiple times for reference ',$bib/@anchor,', will generate link to first entry only')"/>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="compute-rfc-uri">
+        <xsl:with-param name="rfc" select="$rfcEntries[1]/@value"/>
+      </xsl:call-template>
+      <xsl:if test="$ref and $sec!='' and $rfcUrlFragSection and $rfcUrlFragAppendix">
+        <xsl:choose>
+          <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+            <xsl:value-of select="concat('#',$rfcUrlFragAppendix,$sec)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="concat('#',$rfcUrlFragSection,$sec)"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$bib//seriesInfo/@name='Internet-Draft'">
+      <xsl:call-template name="compute-internet-draft-uri">
+        <xsl:with-param name="internet-draft" select="$bib//seriesInfo[@name='Internet-Draft']/@value"/>
+      </xsl:call-template>
+      <xsl:if test="$ref and $sec!='' and $internetDraftUrlFragSection and $internetDraftUrlFragAppendix">
+        <xsl:choose>
+          <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+            <xsl:value-of select="concat('#',$internetDraftUrlFragAppendix,$sec)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="concat('#',$internetDraftUrlFragSection,$sec)"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise />
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="compute-section-number">
+  <xsl:param name="bib"/>
+  <xsl:param name="ref"/>
+  
+  <xsl:variable name="anch" select="substring-after($ref/@x:rel,'#')"/>
+  
+  <xsl:choose>
+    <xsl:when test="$anch=''">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">Not a fragment identifier: <xsl:value-of select="$ref/@x:rel"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="extdoc" select="document($bib/x:source/@href)"/>
+      <xsl:variable name="nodes" select="$extdoc//*[@anchor=$anch]"/>
+      <xsl:if test="not($nodes)">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Anchor '<xsl:value-of select="$anch"/>' in <xsl:value-of select="$bib/@anchor"/> not found in source file '<xsl:value-of select="$bib/x:source/@href"/>'.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:for-each select="$nodes">
+        <xsl:call-template name="get-section-number"/>
+      </xsl:for-each>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="computed-target">
+  <xsl:param name="bib"/>
+  <xsl:param name="ref"/>
+
+  <xsl:variable name="bibtarget">
+    <xsl:choose>
+      <xsl:when test="starts-with($bib/@target,'http://www.rfc-editor.org/info/rfc') or starts-with($bib/@target,'https://www.rfc-editor.org/info/rfc') and $ref and ($ref/@x:sec or $ref/@x:rel or $ref/@section or $ref/@relative)">
+        <!--ignored, use tools.ietf.org link instead -->
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$bib/@target"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <xsl:choose>
+    <xsl:when test="$bibtarget!=''">
+      <xsl:if test="$ref and $ref/@x:sec">
+        <xsl:choose>
+          <xsl:when test="$ref/@x:rel">
+            <xsl:value-of select="concat($bib/@target,$ref/@x:rel)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Can't generate section link for to <xsl:value-of select="$bib/@anchor"/>; no @x:rel specified</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      <xsl:if test="$ref and $ref/@section">
+        <xsl:choose>
+          <xsl:when test="$ref/@relative">
+            <xsl:value-of select="concat($bib/@target,$ref/@relative)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Can't generate section link for to <xsl:value-of select="$bib/@anchor"/>; no @relative specified</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="computed-auto-target">
+        <xsl:with-param name="bib" select="$bib"/>
+        <xsl:with-param name="ref" select="$ref"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="compute-doi">
+  <xsl:choose>
+    <!-- xref seems to be for BCP, not RFC -->
+    <xsl:when test=".//seriesInfo[@name='BCP'] and starts-with(@anchor, 'BCP')" />
+    <xsl:when test=".//seriesInfo[@name='RFC'] and not(.//organization='RFC Errata') and not(@target='http://www.rfc-editor.org' or @target='https://www.rfc-editor.org')">
+      <xsl:variable name="rfc" select=".//seriesInfo[@name='RFC'][1]/@value"/>
+      <xsl:value-of select="concat('10.17487/RFC', format-number($rfc,'#0000'))"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<!-- processed elsewhere -->
+<xsl:template match="displayreference">
+  <xsl:variable name="t" select="@to"/>
+  <xsl:if test="//reference/@anchor=$t or count(//displayreference[@to=$t])!=1">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg">displayreference <xsl:value-of select="$t"/> will create non-unique reference name.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="displayname-for-author">
+  <xsl:param name="not-reversed"/>
+
+  <xsl:variable name="initials">
+    <xsl:call-template name="format-initials"/>
+  </xsl:variable>
+  <xsl:variable name="truncated-initials">
+    <xsl:call-template name="truncate-initials">
+      <xsl:with-param name="initials" select="$initials"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <!-- surname/initials is reversed for last author except when it's the only one -->
+  <xsl:choose>
+    <xsl:when test="$truncated-initials='' and @surname">
+      <xsl:value-of select="@surname"/>
+    </xsl:when>
+    <xsl:when test="position()=last() and position()!=1">
+      <xsl:value-of select="concat($truncated-initials,' ',@surname)" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat(@surname,', ',$truncated-initials)" />
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:if test="@asciiSurname!='' or @asciiInitials!=''">
+    <xsl:text> (</xsl:text>
+    <xsl:variable name="i">
+      <xsl:choose>
+        <xsl:when test="@asciiInitials!=''">
+          <xsl:value-of select="@asciiInitials"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$truncated-initials"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="s">
+      <xsl:choose>
+        <xsl:when test="@asciiSurname!=''">
+          <xsl:value-of select="@asciiSurname"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@surname"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$i=''">
+        <xsl:value-of select="$s"/>
+      </xsl:when>
+      <xsl:when test="$not-reversed">
+        <xsl:value-of select="concat($i,' ',$s)" />
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="concat($s,', ',$i)" />
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:text>)</xsl:text>
+  </xsl:if> 
+  <xsl:if test="@role='editor'">
+    <xsl:text>, Ed.</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="link-ref-title-to">
+  <xsl:choose>
+    <xsl:when test="starts-with(@target,'http://www.rfc-editor.org/info/rfc') or starts-with(@target,'https://www.rfc-editor.org/info/rfc')">
+      <xsl:call-template name="info">
+        <xsl:with-param name="msg">Ignoring @target <xsl:value-of select="@target"/> in link calculation</xsl:with-param>
+      </xsl:call-template>
+      <xsl:call-template name="computed-auto-target">
+        <xsl:with-param name="bib" select="."/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test=".//seriesInfo/@name='RFC' and (@target='http://www.rfc-editor.org' or @target='https://www.rfc-editor.org') and starts-with(front/title,'Errata ID ') and front/author/organization='RFC Errata'">
+      <!-- check for erratum link -->
+      <xsl:variable name="eid" select="normalize-space(substring(front/title,string-length('Errata ID ')))"/>
+      <xsl:call-template name="compute-rfc-erratum-uri">
+        <xsl:with-param name="eid" select="$eid"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="@target">
+      <xsl:if test="normalize-space(@target)=''">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">invalid (empty) target attribute in reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:value-of select="normalize-space(@target)" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="computed-auto-target">
+        <xsl:with-param name="bib" select="."/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="reference">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <!-- check for reference to reference -->
+  <xsl:variable name="anchor" select="@anchor"/>
+  <xsl:choose>
+    <xsl:when test="not(@anchor)">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">missing anchor on reference: <xsl:value-of select="."/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(ancestor::ed:del) and (ancestor::rfc and not(key('xref-item',$anchor)))">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">unused reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="not(ancestor::ed:del) and (not(ancestor::rfc) and not($src//xref[@target=$anchor]))">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">unused (included) reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+
+  <!-- check normative/informative -->
+  <xsl:variable name="t-r-is-normative" select="ancestor-or-self::*[@x:nrm][1]"/>
+  <xsl:variable name="r-is-normative" select="$t-r-is-normative/@x:nrm='true'"/>
+  <xsl:if test="$r-is-normative and not(ancestor::ed:del)">
+    <xsl:variable name="tst">
+      <xsl:for-each select="key('xref-item',$anchor)">
+        <xsl:variable name="t-is-normative" select="ancestor-or-self::*[@x:nrm][1]"/>
+        <xsl:variable name="is-normative" select="$t-is-normative/@x:nrm='true'"/>
+        <xsl:if test="$is-normative">OK</xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:if test="$tst=''">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">all references to the normative reference '<xsl:value-of select="@anchor"/>' appear to be informative</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <xsl:call-template name="check-anchor"/>
+
+  <xsl:variable name="target">
+    <xsl:call-template name="link-ref-title-to"/>
+  </xsl:variable>
+
+  <dt id="{@anchor}">
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:variable name="del-node" select="ancestor::ed:del"/>
+    <xsl:variable name="rep-node" select="ancestor::ed:replace"/>
+    <xsl:variable name="deleted" select="$del-node and ($rep-node/ed:ins)"/>
+    <xsl:for-each select="../..">
+      <xsl:call-template name="insert-issue-pointer">
+        <xsl:with-param name="deleted-anchor" select="$deleted"/>
+      </xsl:call-template>
+    </xsl:for-each>
+    <xsl:call-template name="reference-name"/>
+  </dt>
+
+  <dd>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:for-each select="front/author">
+      <xsl:choose>
+        <xsl:when test="@surname and @surname!=''">
+          <xsl:variable name="displayname">
+            <xsl:call-template name="displayname-for-author">
+              <xsl:with-param name="not-reversed" select="position()=last() and position()!=1"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <xsl:choose>
+            <xsl:when test="address/email and $xml2rfc-linkmailto!='no'">
+              <a href="mailto:{address/email}"><xsl:value-of select="$displayname" /></a>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$displayname" />
+            </xsl:otherwise>
+          </xsl:choose>
+
+          <xsl:choose>
+            <xsl:when test="position()=last() - 1">
+              <xsl:if test="last() &gt; 2">,</xsl:if>
+              <xsl:text> and </xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:text>, </xsl:text>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="organization/text()">
+          <xsl:choose>
+            <xsl:when test="address/uri">
+              <a href="{address/uri}"><xsl:value-of select="organization" /></a>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="organization" />
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:if test="organization/@ascii">
+            <xsl:value-of select="concat(' (',normalize-space(organization/@ascii),')')"/>
+          </xsl:if>
+          <xsl:choose>
+            <xsl:when test="position()=last() - 1">
+              <xsl:if test="last() &gt; 2">,</xsl:if>
+              <xsl:text> and </xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:text>, </xsl:text>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:otherwise />
+      </xsl:choose>
+    </xsl:for-each>
+
+    <xsl:variable name="quoted" select="not(front/title/@x:quotes='false') and not(@quoteTitle='false')"/>
+    <xsl:if test="$quoted">&#8220;</xsl:if>
+    <xsl:choose>
+      <xsl:when test="string-length($target) &gt; 0">
+        <a href="{$target}"><xsl:value-of select="normalize-space(front/title)" /></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="normalize-space(front/title)" />
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="$quoted">&#8221;</xsl:if>
+
+    <xsl:if test="front/title/@ascii!=''">
+      <xsl:text> (</xsl:text>
+      <xsl:if test="$quoted">&#8220;</xsl:if>
+      <xsl:value-of select="normalize-space(front/title/@ascii)" />
+      <xsl:if test="$quoted">&#8221;</xsl:if>
+      <xsl:text>)</xsl:text>
+    </xsl:if> 
+
+    <xsl:variable name="si" select="seriesInfo|front/seriesInfo"/>
+    <xsl:if test="seriesInfo and front/seriesInfo">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">seriesInfo present both on reference and reference/front</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    
+    <xsl:variable name="rfcs" select="count($si[@name='RFC'])"/>
+
+    <xsl:variable name="doi">
+      <xsl:call-template name="compute-doi"/>
+    </xsl:variable>
+
+    <xsl:for-each select="$si">
+      <xsl:text>, </xsl:text>
+      <xsl:choose>
+        <xsl:when test="not(@name) and not(@value) and ./text()"><xsl:value-of select="." /></xsl:when>
+        <xsl:when test="@name='RFC' and $rfcs > 1">
+          <xsl:variable name="uri">
+            <xsl:call-template name="compute-rfc-uri">
+              <xsl:with-param name="rfc" select="@value"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <a href="{$uri}">
+            <xsl:value-of select="@name" />
+            <xsl:if test="@value!=''">&#0160;<xsl:value-of select="@value" /></xsl:if>
+          </a>
+        </xsl:when>
+        <xsl:when test="@name='DOI'">
+          <xsl:variable name="uri">
+            <xsl:call-template name="compute-doi-uri">
+              <xsl:with-param name="doi" select="@value"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <a href="{$uri}">
+            <xsl:value-of select="@name" />
+            <xsl:if test="@value!=''">&#0160;<xsl:value-of select="@value" /></xsl:if>
+          </a>
+          <xsl:if test="$doi!='' and $doi!=@value">
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">Unexpected DOI for RFC, found <xsl:value-of select="@value"/>, expected <xsl:value-of select="$doi"/></xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:when>
+        <xsl:when test="@name='ISBN'">
+          <xsl:variable name="uri">
+            <xsl:call-template name="compute-isbn-uri">
+              <xsl:with-param name="isbn" select="@value"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <a href="{$uri}">
+            <xsl:value-of select="@name" />
+            <xsl:if test="@value!=''">&#0160;<xsl:value-of select="@value" /></xsl:if>
+          </a>
+        </xsl:when>
+        <xsl:when test="@name='Internet-Draft' and $rfcno > 7375">
+          <!-- special case in RFC formatting since 2015 -->            
+          <xsl:text>Work in Progress, </xsl:text>
+          <xsl:value-of select="@value" />
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@name" />
+          <xsl:if test="@value!=''">&#0160;<xsl:value-of select="@value" /></xsl:if>
+          <xsl:if test="translate(@name,$ucase,$lcase)='internet-draft'"> (work in progress)</xsl:if>
+        </xsl:otherwise>
+      </xsl:choose>
+
+      <!-- check that BCP FYI STD RFC are in the right order -->
+      <xsl:if test="(@name='BCP' or @name='FYI' or @name='STD') and preceding-sibling::seriesInfo[@name='RFC']">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">RFC number preceding <xsl:value-of select="@name"/> number in reference '<xsl:value-of select="../@anchor"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+
+    </xsl:for-each>
+
+    <!-- Insert DOI for RFCs -->
+    <xsl:if test="$xml2rfc-ext-insert-doi='yes' and $doi!='' and not($si[@name='DOI'])">
+      <xsl:text>, </xsl:text>
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-doi-uri">
+          <xsl:with-param name="doi" select="$doi"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <a href="{$uri}">DOI&#160;<xsl:value-of select="$doi"/></a>
+    </xsl:if>
+
+    <!-- avoid hacks using seriesInfo when it's not really series information -->
+    <xsl:for-each select="x:prose|refcontent">
+      <xsl:text>, </xsl:text>
+      <xsl:apply-templates/>
+    </xsl:for-each>
+
+    <xsl:if test="not(front/date)">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">&lt;date&gt; missing in reference '<xsl:value-of select="@anchor"/>' (note that it can be empty)</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+
+    <xsl:if test="front/date/@year != ''">
+      <xsl:if test="string(number(front/date/@year)) = 'NaN'">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">date/@year should be a number: '<xsl:value-of select="front/date/@year"/>' in reference '<xsl:value-of select="@anchor"/>'</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:text>, </xsl:text>
+      <xsl:if test="front/date/@month!=''"><xsl:value-of select="front/date/@month" />&#0160;</xsl:if>
+      <xsl:value-of select="front/date/@year" />
+    </xsl:if>
+
+    <xsl:choose>
+      <xsl:when test="string-length(normalize-space(@target)) &gt; 0">
+        <xsl:text>, &lt;</xsl:text>
+        <a href="{normalize-space(@target)}"><xsl:value-of select="normalize-space(@target)"/></a>
+        <xsl:text>&gt;</xsl:text>
+      </xsl:when>
+      <xsl:when test="$xml2rfc-ext-link-rfc-to-info-page='yes' and $si[@name='BCP'] and starts-with(@anchor, 'BCP')">
+        <xsl:text>, &lt;</xsl:text>
+        <xsl:variable name="uri" select="concat('http://www.rfc-editor.org/info/bcp',$si[@name='BCP']/@value)"/>
+        <a href="{$uri}"><xsl:value-of select="$uri"/></a>
+        <xsl:text>&gt;</xsl:text>
+      </xsl:when>
+      <xsl:when test="$xml2rfc-ext-link-rfc-to-info-page='yes' and $si[@name='RFC']">
+        <xsl:text>, &lt;</xsl:text>
+        <xsl:variable name="uri" select="concat('http://www.rfc-editor.org/info/rfc',$si[@name='RFC']/@value)"/>
+        <a href="{$uri}"><xsl:value-of select="$uri"/></a>
+        <xsl:text>&gt;</xsl:text>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+
+    <xsl:text>.</xsl:text>
+
+    <xsl:for-each select="annotation">
+      <br />
+      <xsl:apply-templates />
+    </xsl:for-each>
+
+  </dd>
+
+</xsl:template>
+
+<xsl:template match="references">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:variable name="refseccount" select="count(/rfc/back/references)+count(/rfc/back/ed:replace/ed:ins/references)"/>
+
+  <xsl:choose>
+    <!-- insert pseudo section when needed -->
+    <xsl:when test="not(preceding::references) and $refseccount!=1">
+      <xsl:call-template name="insert-conditional-hrule"/>
+      <section id="{$anchor-pref}references">
+        <xsl:call-template name="insert-conditional-pagebreak"/>
+        <xsl:variable name="sectionNumber">
+          <xsl:call-template name="get-references-section-number"/>
+        </xsl:variable>
+        <xsl:if test="$sectionNumber!=''">
+          <xsl:call-template name="insert-errata">
+            <xsl:with-param name="section" select="$sectionNumber"/>
+          </xsl:call-template>
+        </xsl:if>
+        <h2 id="{$anchor-pref}section.{$sectionNumber}">
+          <a href="#{$anchor-pref}section.{$sectionNumber}">
+            <xsl:call-template name="emit-section-number">
+              <xsl:with-param name="no" select="$sectionNumber"/>
+            </xsl:call-template>
+          </a>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-refparent"/>
+        </h2>
+        <xsl:for-each select=".|following-sibling::references">
+          <xsl:call-template name="make-references">
+            <xsl:with-param name="nested" select="true()"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </section>
+    </xsl:when>
+    <xsl:when test="preceding::references">
+      <!-- already processed -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="make-references">
+        <xsl:with-param name="nested" select="false()"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="make-references">
+  <xsl:param name="nested"/>
+  
+  <xsl:variable name="name">
+    <xsl:if test="ancestor::ed:del">
+      <xsl:text>del-</xsl:text>
+    </xsl:if>
+    <xsl:number level="any"/>
+  </xsl:variable>
+
+  <xsl:variable name="elemtype">
+    <xsl:choose>
+      <xsl:when test="$nested">h3</xsl:when>
+      <xsl:otherwise>h2</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="title">
+    <xsl:choose>
+      <xsl:when test="name">
+        <xsl:if test="@title">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">both @title attribute and name child node present</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:call-template name="render-name">
+          <xsl:with-param name="n" select="name/node()"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="not(@title) or @title=''"><xsl:value-of select="$xml2rfc-refparent"/></xsl:when>
+      <xsl:otherwise><xsl:value-of select="@title"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="sectionNumber">
+    <xsl:call-template name="get-section-number"/>
+  </xsl:variable>
+
+  <xsl:variable name="anchorpostfix">
+    <xsl:if test="$nested">.<xsl:value-of select="$name"/></xsl:if>
+  </xsl:variable>
+
+  <section id="{$anchor-pref}references{$anchorpostfix}">
+    <xsl:if test="$name='1'">
+      <xsl:call-template name="insert-conditional-pagebreak"/>
+    </xsl:if>
+    <xsl:if test="$sectionNumber!=''">
+      <xsl:call-template name="insert-errata">
+        <xsl:with-param name="section" select="$sectionNumber"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:element name="{$elemtype}">
+      <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,'section.',$sectionNumber)"/></xsl:attribute>
+      <a href="#{$anchor-pref}section.{$sectionNumber}">
+        <xsl:call-template name="emit-section-number">
+          <xsl:with-param name="no" select="$sectionNumber"/>
+        </xsl:call-template>
+      </a>
+      <xsl:text> </xsl:text>
+      <xsl:copy-of select="$title"/>
+    </xsl:element>
+  
+    <xsl:variable name="included" select="exslt:node-set($includeDirectives)/myns:include[@in=generate-id(current())]/reference"/>
+    <dl class="{$css-reference}">
+      <xsl:choose>
+        <xsl:when test="$xml2rfc-sortrefs='yes' and $xml2rfc-symrefs!='no'">
+          <xsl:apply-templates select="*|$included">
+            <xsl:sort select="concat(/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor,.//ed:ins//reference/@anchor)" />
+          </xsl:apply-templates>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates select="*|$included"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </dl>
+  </section>
+</xsl:template>
+
+<xsl:template match="xi:include">
+  <xsl:choose>
+    <xsl:when test="not(parent::references)">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'Support for x:include is restricted to child elements of &lt;references>'"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <!-- handled elsewhere -->
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- processed earlier -->
+<xsl:template match="references/name"/>
+
+<xsl:template match="rfc">
+  <xsl:call-template name="check-no-text-content"/>
+  <xsl:variable name="ignored">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="//processing-instruction('rfc-ext')"/>
+      <xsl:with-param name="attr" select="'SANITYCHECK'"/>
+    </xsl:call-template>
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="//processing-instruction('rfc')"/>
+      <xsl:with-param name="attr" select="'SANITYCHECK'"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:variable name="lang">
+    <xsl:call-template name="get-lang" />
+  </xsl:variable>
+
+  <html lang="{$lang}">
+    <head>
+      <title>
+        <xsl:apply-templates select="front/title" mode="get-text-content" />
+      </title>
+      <xsl:call-template name="insertScripts" />
+      <xsl:call-template name="insertCss" />
+      <!-- <link rel="alternate stylesheet" type="text/css" media="screen" title="Plain (typewriter)" href="rfc2629tty.css" /> -->
+
+      <!-- link elements -->
+      <xsl:if test="$xml2rfc-toc='yes'">
+        <link rel="Contents" href="#{$anchor-pref}toc" />
+      </xsl:if>
+      <xsl:if test="$xml2rfc-authorship!='no'">
+        <link rel="Author" href="#{$anchor-pref}authors" />
+      </xsl:if>
+      <xsl:if test="$xml2rfc-private=''">
+        <xsl:choose>
+          <xsl:when test="$no-copylong">
+            <link rel="Copyright" href="#{$anchor-pref}copyrightnotice" />
+          </xsl:when>
+          <xsl:otherwise>
+            <link rel="Copyright" href="#{$anchor-pref}copyright" />
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      <xsl:if test="$has-index">
+        <link rel="Index" href="#{$anchor-pref}index" />
+      </xsl:if>
+      <xsl:apply-templates select="/" mode="links" />
+      <xsl:for-each select="x:link|link">
+        <link>
+          <xsl:choose>
+            <xsl:when test="self::x:link and @basename">
+              <xsl:attribute name="href">
+                <xsl:value-of select="concat(@basename,'.',$outputExtension)"/>
+              </xsl:attribute>
+              <xsl:copy-of select="@rel|@title" />
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:copy-of select="@*" />
+            </xsl:otherwise>
+          </xsl:choose>
+        </link>
+      </xsl:for-each>
+      <xsl:if test="@number">
+        <link rel="Alternate" title="Authoritative ASCII Version" href="http://www.ietf.org/rfc/rfc{@number}.txt" />
+        <link rel="Help" title="RFC-Editor's Status Page" href="http://www.rfc-editor.org/info/rfc{@number}" />
+        <link rel="Help" title="Additional Information on tools.ietf.org" href="https://tools.ietf.org/html/rfc{@number}"/>
+      </xsl:if>
+
+      <!-- generator -->
+      <xsl:variable name="gen">
+        <xsl:call-template name="get-generator" />
+      </xsl:variable>
+      <meta name="generator" content="{$gen}" />
+
+      <!-- keywords -->
+      <xsl:if test="front/keyword">
+        <xsl:variable name="keyw">
+          <xsl:call-template name="get-keywords" />
+        </xsl:variable>
+        <meta name="keywords" content="{$keyw}" />
+      </xsl:if>
+
+      <xsl:if test="$xml2rfc-ext-support-rfc2731!='no'">
+        <!-- Dublin Core Metadata -->
+        <link rel="schema.dcterms" href="http://purl.org/dc/terms/" />
+
+        <!-- DC creator, see RFC2731 -->
+        <xsl:for-each select="front/author">
+          <xsl:variable name="initials">
+            <xsl:call-template name="format-initials"/>
+          </xsl:variable>
+          <meta name="dcterms.creator" content="{concat(@surname,', ',$initials)}" />
+        </xsl:for-each>
+
+        <xsl:if test="$xml2rfc-private=''">
+          <xsl:choose>
+            <xsl:when test="@number">
+              <meta name="dcterms.identifier" content="urn:ietf:rfc:{@number}" />
+            </xsl:when>
+            <xsl:when test="@docName">
+              <meta name="dcterms.identifier" content="urn:ietf:id:{@docName}" />
+            </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+          <meta name="dcterms.issued">
+            <xsl:attribute name="content">
+              <xsl:value-of select="concat($xml2rfc-ext-pub-year,'-',$pub-month-numeric)"/>
+              <xsl:if test="$xml2rfc-ext-pub-day != '' and not(@number)">
+                <xsl:value-of select="concat('-',format-number($xml2rfc-ext-pub-day,'00'))"/>
+              </xsl:if>
+            </xsl:attribute>
+          </meta>
+
+          <xsl:if test="@obsoletes!=''">
+            <xsl:call-template name="rfclist-for-dcmeta">
+              <xsl:with-param name="list" select="@obsoletes"/>
+            </xsl:call-template>
+          </xsl:if>
+        </xsl:if>
+
+        <xsl:if test="front/abstract">
+          <meta name="dcterms.abstract" content="{normalize-space(front/abstract)}" />
+        </xsl:if>
+
+        <xsl:if test="@number">
+          <meta name="dcterms.isPartOf" content="urn:issn:2070-1721" />
+        </xsl:if>
+
+      </xsl:if>
+
+      <!-- this replicates dcterms.abstract, but is used by Google & friends -->
+      <xsl:if test="front/abstract">
+        <meta name="description" content="{normalize-space(front/abstract)}" />
+      </xsl:if>
+    </head>
+
+    <xsl:call-template name="body" />
+  </html>
+</xsl:template>
+
+<xsl:template name="body">
+  <body>
+    <!-- insert onload scripts, when required -->
+    <xsl:variable name="onload">
+      <xsl:if test="$xml2rfc-ext-insert-metadata='yes' and /rfc/@number">getMeta(<xsl:value-of select="/rfc/@number"/>,"rfc.meta");</xsl:if>
+      <xsl:if test="/rfc/x:feedback">initFeedback();</xsl:if>
+      <xsl:if test="$xml2rfc-ext-refresh-from!=''">RfcRefresh.initRefresh()</xsl:if>
+    </xsl:variable>
+    <xsl:if test="$onload!=''">
+      <xsl:attribute name="onload">
+        <xsl:value-of select="$onload"/>
+      </xsl:attribute>
+    </xsl:if>
+
+    <xsl:call-template name="add-start-material" />
+
+    <!-- insert diagnostics -->
+    <xsl:call-template name="insert-diagnostics"/>
+
+    <xsl:apply-templates select="front" />
+    <xsl:apply-templates select="middle" />
+    <xsl:call-template name="back" />
+
+    <xsl:call-template name="add-end-material" />
+  </body>
+</xsl:template>
+
+<xsl:template match="t">
+  <xsl:param name="inherited-self-link"/>
+
+  <xsl:variable name="textcontent" select="normalize-space(.)"/>
+  <xsl:variable name="endswith" select="substring($textcontent,string-length($textcontent))"/>
+  <xsl:variable name="keepwithnext" select="$endswith=':'"/>
+
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+
+  <xsl:variable name="stype">
+    <xsl:choose>
+      <xsl:when test="ancestor::abstract">abstract</xsl:when>
+      <xsl:when test="ancestor::note">note</xsl:when>
+      <xsl:when test="ancestor::boilerplate">boilerplate</xsl:when>
+      <xsl:otherwise>section</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:if test="preceding-sibling::section or preceding-sibling::appendix">
+    <xsl:call-template name="inline-warning">
+      <xsl:with-param name="msg">The paragraph below is misplaced; maybe a section is closed in the wrong place: </xsl:with-param>
+      <xsl:with-param name="msg2"><xsl:value-of select="."/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <div>
+    <xsl:if test="$p!='' and not(ancestor::list) and not(ancestor::ol) and not(ancestor::ul) and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <xsl:attribute name="id"><xsl:value-of select="concat($anchor-pref,$stype,'.',$p)"/></xsl:attribute>
+    </xsl:if>
+    <xsl:if test="$keepwithnext">
+      <xsl:attribute name="class">avoidbreakafter</xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates mode="t-content" select="node()[1]">
+      <xsl:with-param name="inherited-self-link" select="$inherited-self-link"/>
+      <xsl:with-param name="anchor" select="@anchor"/>
+    </xsl:apply-templates>
+  </div>
+</xsl:template>
+
+<!-- for t-content, dispatch to default templates if it's block-level content -->
+<xsl:template mode="t-content" match="list|figure|texttable">
+  <!-- <xsl:comment>t-content block-level</xsl:comment>  -->
+  <xsl:apply-templates select="." />
+  <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content" />
+</xsl:template>
+
+<!-- ... otherwise group into p elements -->
+<xsl:template mode="t-content" match="*|node()">
+  <xsl:param name="inherited-self-link"/>
+  <xsl:param name="anchor"/>
+
+  <xsl:variable name="p">
+    <xsl:choose>
+      <xsl:when test="self::text()">
+        <xsl:for-each select="..">
+          <xsl:call-template name="get-paragraph-number" />
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="get-paragraph-number" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <!-- do not open a new p element if this is a whitespace-only text node and no siblings follow -->
+  <xsl:if test="not(self::text() and normalize-space(.)='' and not(following-sibling::node()))">
+    <xsl:variable name="textcontent">
+      <xsl:apply-templates mode="t-content2" select="." />
+    </xsl:variable>
+
+    <xsl:if test="normalize-space($textcontent)!=''">
+      <p>
+        <xsl:if test="$anchor!=''">
+          <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+        </xsl:if>
+        <xsl:variable name="stype">
+          <xsl:choose>
+            <xsl:when test="ancestor::abstract">abstract</xsl:when>
+            <xsl:when test="ancestor::note">note</xsl:when>
+            <xsl:when test="ancestor::boilerplate">boilerplate</xsl:when>
+            <xsl:otherwise>section</xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <xsl:variable name="anch">
+          <xsl:if test="$p!='' and not(ancestor::ed:del) and not(ancestor::ed:ins) and not(ancestor::li) and not(ancestor::x:lt) and not(preceding-sibling::node())">
+            <xsl:value-of select="concat($anchor-pref,$stype,'.',$p)"/>
+          </xsl:if>
+        </xsl:variable>
+        <xsl:call-template name="insertInsDelClass"/>
+        <xsl:call-template name="editingMark" />
+        <xsl:apply-templates mode="t-content2" select="." />
+        <xsl:if test="$xml2rfc-ext-paragraph-links='yes'">
+          <xsl:if test="$anch!=''">
+            <a class='self' href='#{$anch}'>&#xb6;</a>
+          </xsl:if>
+          <xsl:if test="$inherited-self-link!=''">
+            <a class='self' href='#{$inherited-self-link}'>&#xb6;</a>
+          </xsl:if>
+        </xsl:if>
+      </p>
+    </xsl:if>
+  </xsl:if>
+  <xsl:apply-templates mode="t-content" select="following-sibling::*[self::list or self::figure or self::texttable][1]" />
+</xsl:template>
+
+<xsl:template mode="t-content2" match="*">
+  <xsl:apply-templates select="." />
+  <xsl:if test="not(following-sibling::node()[1] [self::list or self::figure or self::texttable])">
+    <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content2" />
+  </xsl:if>
+</xsl:template>
+
+<xsl:template mode="t-content2" match="text()">
+  <xsl:apply-templates select="." />
+  <xsl:if test="not(following-sibling::node()[1] [self::list or self::figure or self::texttable])">
+    <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content2" />
+  </xsl:if>
+</xsl:template>
+
+<xsl:template mode="t-content2" match="comment()|processing-instruction()">
+  <xsl:apply-templates select="." />
+  <xsl:if test="not(following-sibling::node()[1] [self::list or self::figure or self::texttable])">
+    <xsl:apply-templates select="following-sibling::node()[1]" mode="t-content2" />
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="title">
+  <xsl:variable name="t" select="normalize-space(.)"/>
+  <xsl:variable name="tlen" select="string-length($t)"/>
+  <xsl:variable name="alen" select="string-length(@abbrev)"/>
+
+  <xsl:if test="@abbrev and $alen > 40">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">title/@abbrev too long (max 40 characters)</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$tlen > 40 and (not(@abbrev) or @abbrev='')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">title too long, should supply title/@abbrev attribute with less than 40 characters</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:if test="$tlen &lt;= 40 and @abbrev!=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">title/@abbrev was specified despite the title being short enough (<xsl:value-of select="$tlen"/>)</xsl:with-param>
+      <xsl:with-param name="msg2">Title: '<xsl:value-of select="normalize-space($t)"/>', abbreviated title='<xsl:value-of select="@abbrev"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:apply-templates />
+</xsl:template>
+
+<xsl:template name="insertTitle">
+  <xsl:choose>
+    <xsl:when test="@ed:old-title">
+      <del>
+        <xsl:if test="ancestor-or-self::*[@ed:entered-by] and @ed:datetime">
+          <xsl:attribute name="title"><xsl:value-of select="concat(@ed:datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="@ed:old-title"/>
+      </del>
+      <ins>
+        <xsl:if test="ancestor-or-self::*[@ed:entered-by] and @ed:datetime">
+          <xsl:attribute name="title"><xsl:value-of select="concat(@ed:datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="@title"/>
+      </ins>
+    </xsl:when>
+    <xsl:when test="name">
+      <xsl:if test="@title">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">both @title attribute and name child node present</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:call-template name="render-name">
+        <xsl:with-param name="n" select="name/node()"/>
+        <xsl:with-param name="strip-links" select="not(ancestor-or-self::figure)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="@title"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- irefs that are section-level thus can use the section anchor -->
+<xsl:variable name="section-level-irefs" select="//section/iref[count(preceding-sibling::*[not(self::iref) and not(self::x:anchor-alias) and not(self::name)])=0]"/>
+
+<xsl:template match="section|appendix">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:if test="self::appendix">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">The "appendix" element is deprecated, use "section" inside "back" instead.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="sectionNumber">
+    <xsl:choose>
+      <xsl:when test="ancestor::boilerplate"></xsl:when>
+      <xsl:otherwise><xsl:call-template name="get-section-number" /></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:if test="not(ancestor::section) and not(ancestor::boilerplate)">
+    <xsl:call-template name="insert-conditional-hrule"/>
+  </xsl:if>
+
+  <xsl:variable name="elemtype">
+    <xsl:choose>
+      <xsl:when test="count(ancestor::section) &lt;= 3">h<xsl:value-of select="2 + count(ancestor::section)"/></xsl:when>
+      <xsl:otherwise>h6</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:if test="$xml2rfc-ext-insert-metadata='yes' and $rfcno!='' and @anchor='rfc.status'">
+    <aside id="{$anchor-pref}meta" class="{$css-docstatus}"></aside>
+  </xsl:if>
+
+  <xsl:variable name="classes"><xsl:if test="@removeInRFC='true'">rfcEditorRemove</xsl:if></xsl:variable>
+
+  <section>
+    <xsl:call-template name="copy-anchor"/>
+
+    <xsl:if test="normalize-space($classes)!=''">
+      <xsl:attribute name="class"><xsl:value-of select="normalize-space($classes)"/></xsl:attribute>
+    </xsl:if>
+    
+    <xsl:if test="$sectionNumber!=''">
+      <xsl:call-template name="insert-errata">
+        <xsl:with-param name="section" select="$sectionNumber"/>
+      </xsl:call-template>
+    </xsl:if>
+    
+    <xsl:element name="{$elemtype}">
+      <xsl:if test="$sectionNumber!=''">
+        <xsl:attribute name="id"><xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$sectionNumber"/></xsl:attribute>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="$sectionNumber='1' or $sectionNumber='A'">
+          <!-- pagebreak, this the first section -->
+          <xsl:attribute name="class">np</xsl:attribute>
+        </xsl:when>
+        <xsl:when test="not(ancestor::section) and not(ancestor::boilerplate)">
+          <xsl:call-template name="insert-conditional-pagebreak"/>
+        </xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+  
+      <xsl:call-template name="insertInsDelClass" />
+  
+      <xsl:if test="$sectionNumber!='' and not(contains($sectionNumber,'unnumbered-'))">
+        <a href="#{$anchor-pref}section.{$sectionNumber}">
+          <xsl:call-template name="emit-section-number">
+            <xsl:with-param name="no" select="$sectionNumber"/>
+          </xsl:call-template>
+        </a>
+        <xsl:text>&#0160;</xsl:text>
+      </xsl:if>
+  
+      <!-- issue tracking? -->
+      <xsl:if test="@ed:resolves">
+        <xsl:call-template name="insert-issue-pointer"/>
+      </xsl:if>
+
+      <xsl:call-template name="check-anchor"/>
+      <xsl:variable name="anchor">
+        <xsl:choose>
+          <xsl:when test="@anchor"><xsl:value-of select="@anchor"/></xsl:when>
+          <xsl:otherwise><xsl:call-template name="sluggy-anchor"/></xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      
+      <xsl:choose>
+        <xsl:when test="$anchor!=''">
+          <a href="#{$anchor}"><xsl:call-template name="insertTitle"/></a>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="insertTitle"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:element>
+
+    <xsl:if test="@removeInRFC='true' and t[1]!=$section-removeInRFC">
+      <xsl:variable name="t">
+        <t><xsl:value-of select="$section-removeInRFC"/></t>
+      </xsl:variable>
+      <xsl:variable name="link" select="concat($anchor-pref,'section.',$sectionNumber,'.p.1')"/>
+      <div id="{$link}">
+        <xsl:apply-templates mode="t-content" select="exslt:node-set($t)//text()">
+          <xsl:with-param name="inherited-self-link" select="$link"/>
+        </xsl:apply-templates>
+      </div>
+    </xsl:if>
+
+    <!-- continue with all child elements but the irefs processed above -->
+    <xsl:for-each select="*">
+      <xsl:if test="count(.|$section-level-irefs)!=count($section-level-irefs)">
+        <xsl:apply-templates select="."/>
+      </xsl:if>
+    </xsl:for-each>
+  </section>
+</xsl:template>
+
+<!-- errata handling -->
+<xsl:template name="insert-errata">
+  <xsl:param name="section"/>
+  <xsl:variable name="es" select="$errata-parsed[section=$section or (not(section) and $section='1')]"/>
+  <xsl:if test="$es">
+    <aside class="{$css-erratum}">
+      <xsl:for-each select="$es">
+        <xsl:sort select="@eid" data-type="number"/>
+        <div>
+          <xsl:variable name="tooltip">
+            <xsl:value-of select="@reported-by"/>
+            <xsl:text>, </xsl:text>
+            <xsl:value-of select="@reported"/>
+            <xsl:if test="@type"> (<xsl:value-of select="@type"/>)</xsl:if>
+          </xsl:variable>
+          <xsl:variable name="uri">
+            <xsl:call-template name="compute-rfc-erratum-uri">
+              <xsl:with-param name="eid" select="@eid"/>
+            </xsl:call-template>
+          </xsl:variable>
+          <a href="{$uri}" title="{$tooltip}">Erratum <xsl:value-of select="@eid"/></a>
+          <xsl:choose>
+            <xsl:when test="@status='Verified'"><xsl:text> </xsl:text><span title="verified">&#x2714;</span></xsl:when>
+            <xsl:when test="@status='Reported'"><xsl:text> </xsl:text><span title="reported">&#x2709;</span></xsl:when>
+            <xsl:when test="@status='Held for Document Update'"><xsl:text> </xsl:text><span title="held for update">&#x2700;</span></xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </div>
+      </xsl:for-each>
+    </aside>
+  </xsl:if>
+</xsl:template>
+
+<!-- already processed by insertTitle -->
+<xsl:template match="note/name"/>
+<xsl:template match="section/name"/>
+
+<xsl:template match="spanx[@style='emph' or not(@style)]|em">
+  <em>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </em>
+</xsl:template>
+
+<xsl:template match="spanx[@style='verb' or @style='vbare']|tt">
+  <span class="tt">
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </span>
+</xsl:template>
+
+<xsl:template match="spanx[@style='strong']|strong">
+  <strong>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </strong>
+</xsl:template>
+
+<xsl:template match="spanx[@style!='']" priority="0.1">
+  <xsl:call-template name="warning">
+    <xsl:with-param name="msg">unknown spanx style attribute '<xsl:value-of select="@style"/>' ignored</xsl:with-param>
+  </xsl:call-template>
+  <span>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates />
+  </span>
+</xsl:template>
+
+<xsl:template name="insert-blank-lines">
+  <xsl:param name="no"/>
+  <xsl:choose>
+    <xsl:when test="$no >= $xml2rfc-ext-vspace-pagebreak">
+      <br/>
+      <!-- done; this probably was an attempt to generate a pagebreak -->
+    </xsl:when>
+    <xsl:when test="$no &lt;= 0">
+      <br/>
+      <!-- done -->
+    </xsl:when>
+    <xsl:otherwise>
+      <br/>
+      <xsl:call-template name="insert-blank-lines">
+        <xsl:with-param name="no" select="$no - 1"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="vspace[not(@blankLines)]">
+  <br />
+</xsl:template>
+
+<xsl:template match="vspace">
+  <xsl:call-template name="insert-blank-lines">
+    <xsl:with-param name="no" select="@blankLines"/>
+  </xsl:call-template>
+</xsl:template>
+
+<!-- keep the root for the case when we process XSLT-inline markup -->
+<xsl:variable name="src" select="/" />
+
+<xsl:template name="render-section-ref">
+  <xsl:param name="from" />
+  <xsl:param name="to" />
+
+  <xsl:variable name="refname">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-section-type">
+        <xsl:with-param name="prec" select="$from/preceding-sibling::node()[1]" />
+      </xsl:call-template>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name="refnum">
+    <xsl:for-each select="$to">
+      <xsl:call-template name="get-section-number" />
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name="title">
+    <xsl:choose>
+      <xsl:when test="$to/name">
+        <xsl:value-of select="$to/name"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$to/@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:attribute name="title">
+    <xsl:value-of select="$title" />
+  </xsl:attribute>
+  <xsl:choose>
+    <xsl:when test="$from/@format='counter'">
+      <xsl:value-of select="$refnum"/>
+    </xsl:when>
+    <xsl:when test="$from/@format='title'">
+    <xsl:choose>
+      <xsl:when test="$to/name">
+        <xsl:apply-templates select="$to/name/node()"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$to/@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    </xsl:when>
+    <xsl:when test="$from/@format='none'">
+      <!-- Nothing to do -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="normalize-space(concat($refname,'&#160;',$refnum))"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-section-xref-format">
+  <xsl:param name="default"/>
+  <xsl:choose>
+    <xsl:when test="@sectionFormat">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">@sectionFormat is deprecated, use @x:fmt instead</xsl:with-param>
+      </xsl:call-template>
+      <xsl:if test="@x:fmt">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">both @x:fmt and @sectionFormat specified</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="@sectionFormat='parens' or @sectionFormat='of' or @sectionFormat='comma' or @sectionFormat='section' or @sectionFormat='number-only'">
+          <xsl:value-of select="@sectionFormat"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">unknown format for @sectionFormat</xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="@x:fmt">
+      <xsl:choose>
+        <xsl:when test="@x:fmt='()'">parens</xsl:when>
+        <xsl:when test="@x:fmt='of'">of</xsl:when>
+        <xsl:when test="@x:fmt=','">comma</xsl:when>
+        <xsl:when test="@x:fmt='none'">none</xsl:when>
+        <xsl:when test="@x:fmt='sec'">section</xsl:when>
+        <xsl:when test="@x:fmt='number'">number-only</xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">unknown format for @x:fmt</xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$default"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-section-xref-section">
+  <xsl:choose>
+    <xsl:when test="@section">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">@section is deprecated, use @x:sec instead</xsl:with-param>
+      </xsl:call-template>
+      <xsl:if test="@x:sec">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">both @x:sec and @section specified</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:value-of select="@section"/>
+    </xsl:when>
+    <xsl:when test="@x:sec">
+      <xsl:value-of select="@x:sec"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="xref[node()]">
+
+  <xsl:variable name="target" select="@target" />
+  <xsl:variable name="node" select="key('anchor-item',$target)" />
+  <xsl:variable name="anchor"><xsl:value-of select="$anchor-pref"/>xref.<xsl:value-of select="@target"/>.<xsl:number level="any" count="xref[@target=$target]"/></xsl:variable>
+
+  <xsl:variable name="sfmt">
+    <xsl:call-template name="get-section-xref-format"/>
+  </xsl:variable>
+
+  <xsl:variable name="ssec">
+    <xsl:call-template name="get-section-xref-section"/>
+  </xsl:variable>
+
+  <xsl:choose>
+    <!-- $sfmt='none': do not generate any links -->
+    <xsl:when test="$sfmt='none'">
+      <xsl:choose>
+        <xsl:when test="$node/self::reference">
+          <cite title="{normalize-space($node/front/title)}">
+            <xsl:if test="$xml2rfc-ext-include-references-in-index='yes'">
+              <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+            </xsl:if>
+            <!-- insert id when a backlink to this xref is needed in the index -->
+            <xsl:if test="//iref[@x:for-anchor=$target] | //iref[@x:for-anchor='' and ../@anchor=$target]">
+              <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+            </xsl:if>
+            <xsl:apply-templates/>
+          </cite>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+
+    <!-- Other $sfmt values than "none": unsupported -->
+    <xsl:when test="$sfmt!='' and $sfmt!='none'">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('unknown xref section format extension: ',$sfmt)"/>
+      </xsl:call-template>
+    </xsl:when>
+
+    <!-- Section links -->
+    <xsl:when test="$node/self::section or $node/self::appendix">
+      <xsl:choose>
+        <xsl:when test="@format='none'">
+          <a href="#{@target}">
+            <!-- insert id when a backlink to this xref is needed in the index -->
+            <xsl:if test="//iref[@x:for-anchor=$target] | //iref[@x:for-anchor='' and ../@anchor=$target]">
+              <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+            </xsl:if>
+            <xsl:apply-templates/>
+          </a>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates/>
+          <xsl:text> (</xsl:text>
+          <a href="#{@target}">
+            <!-- insert id when a backlink to this xref is needed in the index -->
+            <xsl:if test="//iref[@x:for-anchor=$target] | //iref[@x:for-anchor='' and ../@anchor=$target]">
+              <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+            </xsl:if>
+            <xsl:call-template name="render-section-ref">
+              <xsl:with-param name="from" select="."/>
+              <xsl:with-param name="to" select="$node"/>
+            </xsl:call-template>
+          </a>
+          <xsl:text>)</xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+
+    <xsl:when test="$node/self::cref and $xml2rfc-comments='no'">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg">xref to cref, but comments aren't included in the output</xsl:with-param>
+      </xsl:call-template>
+    </xsl:when>
+
+    <xsl:otherwise>
+      <!-- check normative/informative -->
+      <xsl:variable name="t-is-normative" select="ancestor-or-self::*[@x:nrm][1]"/>
+      <xsl:variable name="is-normative" select="$t-is-normative/@x:nrm='true'"/>
+      <xsl:if test="count($node)=1 and $is-normative">
+        <xsl:variable name="t-r-is-normative" select="$node/ancestor-or-self::*[@x:nrm][1]"/>
+        <xsl:variable name="r-is-normative" select="$t-r-is-normative/@x:nrm='true'"/>
+        <xsl:if test="not($r-is-normative)">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg" select="concat('Potentially normative reference to ',@target,' not referenced normatively')"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:if>
+
+      <a href="#{$target}">
+        <xsl:if test="@format='none'">
+          <xsl:if test="$xml2rfc-ext-include-references-in-index='yes'">
+            <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+          </xsl:if>
+        </xsl:if>
+        
+        <xsl:apply-templates />
+      </a>
+      <xsl:if test="not(@format='none')">
+        <xsl:for-each select="$src/rfc/back/references//reference[@anchor=$target]">
+          <xsl:text> </xsl:text>
+          <cite title="{normalize-space(front/title)}">
+            <xsl:if test="$xml2rfc-ext-include-references-in-index='yes'">
+              <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+            </xsl:if>
+            <xsl:call-template name="reference-name"/>
+          </cite>
+        </xsl:for-each>
+      </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:key name="iref-xanch" match="iref[@x:for-anchor]" use="@x:for-anchor"/>
+
+<xsl:template match="xref[not(node())]">
+
+  <xsl:variable name="xref" select="."/>
+  <xsl:variable name="anchor"><xsl:value-of select="$anchor-pref"/>xref.<xsl:value-of select="$xref/@target"/>.<xsl:number level="any" count="xref[@target=$xref/@target]"/></xsl:variable>
+  
+  <xsl:variable name="sfmt">
+    <xsl:call-template name="get-section-xref-format">
+      <xsl:with-param name="default">
+        <xsl:choose>
+          <xsl:when test="ancestor::artwork">comma</xsl:when>
+          <xsl:otherwise>of</xsl:otherwise>
+        </xsl:choose>
+      </xsl:with-param>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:variable name="ssec">
+    <xsl:call-template name="get-section-xref-section"/>
+  </xsl:variable>
+
+  <!-- ensure we have the right context, this <xref> may be processed from within the boilerplate -->
+  <xsl:for-each select="$src">
+
+    <xsl:variable name="node" select="key('anchor-item',$xref/@target)|exslt:node-set($includeDirectives)//reference[@anchor=$xref/@target]"/>
+    <xsl:if test="count($node)=0 and not($node/ancestor::ed:del)">
+      <xsl:for-each select="$xref">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg" select="concat('Undefined target: ',$xref/@target)"/>
+        </xsl:call-template>
+      </xsl:for-each>
+    </xsl:if>
+
+    <xsl:choose>
+
+      <!-- Section links -->
+      <xsl:when test="$node/self::section or $node/self::appendix">
+        <a href="#{$xref/@target}">
+          <!-- insert id when a backlink to this xref is needed in the index -->
+          <xsl:if test="key('iref-xanch',$xref/@target) | key('iref-xanch','')[../@anchor=$xref/@target]">
+            <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+          </xsl:if>
+          <xsl:call-template name="render-section-ref">
+            <xsl:with-param name="from" select="$xref"/>
+            <xsl:with-param name="to" select="$node"/>
+          </xsl:call-template>
+        </a>
+      </xsl:when>
+
+      <!-- Figure links -->
+      <xsl:when test="$node/self::figure">
+        <a href="#{$xref/@target}">
+          <xsl:variable name="figcnt">
+            <xsl:for-each select="$node">
+              <xsl:call-template name="get-figure-number"/>
+            </xsl:for-each>
+          </xsl:variable>
+          <xsl:choose>
+            <xsl:when test="$xref/@format='counter'">
+              <xsl:value-of select="$figcnt" />
+            </xsl:when>
+            <xsl:when test="$xref/@format='none'">
+              <!-- Nothing to do -->
+            </xsl:when>
+            <xsl:when test="$xref/@format='title'">
+              <xsl:value-of select="$node/@title" />
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="normalize-space(concat('Figure&#160;',$figcnt))"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </a>
+      </xsl:when>
+
+      <!-- Table links -->
+      <xsl:when test="$node/self::texttable or $node/self::table">
+        <a href="#{$xref/@target}">
+          <xsl:variable name="tabcnt">
+            <xsl:for-each select="$node">
+              <xsl:call-template name="get-table-number"/>
+            </xsl:for-each>
+          </xsl:variable>
+          <xsl:choose>
+            <xsl:when test="$xref/@format='counter'">
+              <xsl:value-of select="$tabcnt" />
+            </xsl:when>
+            <xsl:when test="$xref/@format='none'">
+              <!-- Nothing to do -->
+            </xsl:when>
+            <xsl:when test="$xref/@format='title'">
+              <xsl:choose>
+                <xsl:when test="$node/self::table">
+                  <xsl:call-template name="render-name-ref">
+                    <xsl:with-param name="n" select="$node/name/node()"/>
+                  </xsl:call-template>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="$node/@title" />
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="normalize-space(concat('Table&#160;',$tabcnt))"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </a>
+      </xsl:when>
+
+      <!-- Paragraph links -->
+      <xsl:when test="$node/self::t or $node/self::aside or $node/self::blockquote or $node/self::dl or $node/self::ol or $node/self::ul or $node/self::dt or $node/self::li">
+        <a href="#{$xref/@target}">
+          <xsl:variable name="tcnt">
+            <xsl:for-each select="$node">
+              <xsl:call-template name="get-paragraph-number" />
+            </xsl:for-each>
+          </xsl:variable>
+          <xsl:variable name="pparent" select="$node/.."/>
+          <xsl:variable name="listtype">
+            <xsl:choose>
+              <xsl:when test="$pparent/self::list">
+                <xsl:value-of select="$pparent/@style"/>
+              </xsl:when>
+              <xsl:when test="$pparent/self::dl">definition</xsl:when> 
+              <xsl:when test="$pparent/self::ol[@type='a']">letters</xsl:when> 
+              <xsl:when test="$pparent/self::ol[@type='A']">Letters</xsl:when> 
+              <xsl:when test="$pparent/self::ol[@type='i']">rnumbers</xsl:when> 
+              <xsl:when test="$pparent/self::ol[@type='I']">Rnumbers</xsl:when> 
+              <xsl:when test="$pparent/self::ol[string-length(@type)>1]">format <xsl:value-of select="$pparent/self::ol/@type"/></xsl:when> 
+              <xsl:when test="$pparent/self::ol">numbers</xsl:when> 
+              <xsl:otherwise></xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <xsl:variable name="s">
+            <xsl:choose>
+              <xsl:when test="$pparent/@group">
+                <xsl:call-template name="ol-start">
+                  <xsl:with-param name="node" select="$pparent"/>
+                </xsl:call-template>
+              </xsl:when>
+              <xsl:when test="$pparent/@start">
+                <xsl:value-of select="$pparent/@start"/>
+              </xsl:when>
+              <xsl:otherwise>1</xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <xsl:variable name="n">
+            <xsl:for-each select="$node">
+              <xsl:number/>
+            </xsl:for-each>
+          </xsl:variable>
+          <xsl:variable name="format">
+            <xsl:choose>
+              <xsl:when test="$listtype='letters'">a</xsl:when>
+              <xsl:when test="$listtype='Letters'">A</xsl:when>
+              <xsl:when test="$listtype='rnumbers'">i</xsl:when>
+              <xsl:when test="$listtype='Rnumbers'">I</xsl:when>
+              <xsl:otherwise>1</xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <xsl:variable name="listindex">
+            <xsl:choose>
+              <xsl:when test="starts-with($listtype,'format ')">
+                <xsl:call-template name="expand-format-percent">
+                  <xsl:with-param name="format" select="substring-after($listtype,'format ')"/>
+                  <xsl:with-param name="pos" select="$n + $s - 1"/>
+                </xsl:call-template>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:number value="$n + $s - 1" format="{$format}"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <xsl:choose>
+            <xsl:when test="$xref/@format='counter'">
+              <xsl:choose>
+                <xsl:when test="$listtype!='' and $listindex!=''">
+                  <xsl:value-of select="$listindex"/>
+                </xsl:when>
+                <xsl:when test="$listtype!='' and $listindex=''">
+                  <xsl:call-template name="warning">
+                    <xsl:with-param name="msg" select="concat('Use of format=counter for unsupported list type ',$listtype)"/>
+                  </xsl:call-template>
+                  <xsl:value-of select="$tcnt"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="$tcnt"/>              
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:when test="$xref/@format='none'">
+              <!-- Nothing to do -->
+            </xsl:when>
+            <xsl:when test="$xref/@format='title'">
+              <xsl:choose>
+                <xsl:when test="$node/self::dt">
+                  <xsl:apply-templates select="$node/node()"/>
+                </xsl:when>
+                <xsl:when test="$node/@hangText">
+                  <xsl:value-of select="$node/@hangText"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="$node/@title" />
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:variable name="pn" select="normalize-space(substring-after($tcnt,'p.'))"/>
+              <xsl:text>Paragraph&#160;</xsl:text>
+              <xsl:choose>
+                <xsl:when test="$pn=''">
+                  <xsl:text>?</xsl:text>
+                  <xsl:call-template name="warning">
+                    <xsl:with-param name="msg" select="concat('No paragraph number for link target ',$xref/@target)"/>
+                  </xsl:call-template>
+                </xsl:when>
+                <xsl:otherwise><xsl:value-of select="$pn"/></xsl:otherwise>
+              </xsl:choose>
+            </xsl:otherwise>
+          </xsl:choose>
+        </a>
+      </xsl:when>
+
+      <!-- Comment links -->
+      <xsl:when test="$node/self::cref">
+        <xsl:choose>
+          <xsl:when test="$xml2rfc-comments!='no'">
+            <a href="#{$xref/@target}">
+              <xsl:variable name="name">
+                <xsl:for-each select="$node">
+                  <xsl:call-template name="get-comment-name" />
+                </xsl:for-each>
+              </xsl:variable>
+              <xsl:choose>
+                <xsl:when test="$xref/@format='counter'">
+                  <xsl:value-of select="$name" />
+                </xsl:when>
+                <xsl:when test="$xref/@format='none'">
+                  <!-- Nothing to do -->
+                </xsl:when>
+                <xsl:when test="$xref/@format='title'">
+                  <xsl:value-of select="$node/@title" />
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:value-of select="normalize-space(concat('Comment&#160;',$name))"/>
+                </xsl:otherwise>
+              </xsl:choose>
+            </a>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:for-each select="$xref">
+              <xsl:call-template name="error">
+                <xsl:with-param name="msg">xref to cref, but comments aren't included in the output</xsl:with-param>
+              </xsl:call-template>
+            </xsl:for-each>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+
+      <!-- Reference links -->
+      <xsl:when test="$node/self::reference">
+
+        <!-- check normative/informative -->
+        <xsl:variable name="t-is-normative" select="$xref/ancestor-or-self::*[@x:nrm][1]"/>
+        <xsl:variable name="is-normative" select="$t-is-normative/@x:nrm='true'"/>
+        <xsl:if test="count($node)=1 and $is-normative">
+          <xsl:variable name="t-r-is-normative" select="$node/ancestor-or-self::*[@x:nrm][1]"/>
+          <xsl:variable name="r-is-normative" select="$t-r-is-normative/@x:nrm='true'"/>
+          <xsl:if test="not($r-is-normative)">
+            <xsl:for-each select="$xref">
+              <xsl:call-template name="warning">
+                <xsl:with-param name="msg" select="concat('Potentially normative reference to ',$xref/@target,' not referenced normatively')"/>
+              </xsl:call-template>
+            </xsl:for-each>
+          </xsl:if>
+        </xsl:if>
+
+        <xsl:variable name="href">
+          <xsl:call-template name="computed-target">
+            <xsl:with-param name="bib" select="$node"/>
+            <xsl:with-param name="ref" select="$xref"/>
+          </xsl:call-template>
+        </xsl:variable>
+
+        <xsl:variable name="sec">
+          <xsl:choose>
+            <xsl:when test="starts-with($xref/@x:rel,'#') and $ssec=''">
+              <xsl:call-template name="compute-section-number">
+                <xsl:with-param name="bib" select="$node"/>
+                <xsl:with-param name="ref" select="$xref"/>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="$xref/@x:rel and not(starts-with($xref/@x:rel,'#')) and $ssec=''">
+              <xsl:call-template name="error">
+                <xsl:with-param name="msg">x:rel attribute '<xsl:value-of select="$xref/@x:rel"/>' in reference to <xsl:value-of select="$node/@anchor"/> is expected to start with '#'.</xsl:with-param>
+              </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$ssec"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+
+        <xsl:variable name="secterm">
+          <xsl:choose>
+            <!-- starts with letter? -->
+            <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">Appendix</xsl:when>
+            <xsl:otherwise>Section</xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+
+        <xsl:variable name="title">
+          <xsl:choose>
+            <xsl:when test="starts-with($xref/@x:rel,'#') and $ssec='' and $node/x:source/@href">
+              <xsl:variable name="extdoc" select="document($node/x:source/@href)"/>
+              <xsl:variable name="nodes" select="$extdoc//*[@anchor=substring-after($xref//@x:rel,'#')]"/>
+              <xsl:if test="not($nodes)">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg">Anchor '<xsl:value-of select="substring-after($xref//@x:rel,'#')"/>' not found in <xsl:value-of select="$node/x:source/@href"/>.</xsl:with-param>
+                </xsl:call-template>
+              </xsl:if>
+              <xsl:for-each select="$nodes">
+                <xsl:value-of select="@title"/>
+              </xsl:for-each>
+            </xsl:when>
+            <xsl:otherwise />
+          </xsl:choose>
+        </xsl:variable>
+
+        <!--
+        Formats:
+
+          parens  [XXXX] (Section SS)
+          comma   [XXXX], Section SS
+          of      Section SS of [XXXX]
+          sec     Section SS
+          number  SS
+        -->
+
+        <xsl:if test="$sfmt!='' and not($sfmt='of' or $sfmt='section' or $sfmt='number-only' or $sfmt='parens' or $sfmt='comma')">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('unknown xref section format extension: ',$sfmt)"/>
+          </xsl:call-template>
+        </xsl:if>
+
+        <xsl:if test="$sec!=''">
+
+          <xsl:choose>
+            <xsl:when test="$sfmt='of' or $sfmt='section'">
+              <xsl:choose>
+                <xsl:when test="$href!=''">
+                  <a href="{$href}">
+                    <xsl:if test="$title!=''">
+                      <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+                    </xsl:if>
+                    <xsl:if test="$sfmt='section' and $xml2rfc-ext-include-references-in-index='yes'">
+                      <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+                    </xsl:if>
+                    <xsl:value-of select="$secterm"/>
+                    <xsl:text> </xsl:text>
+                    <xsl:value-of select="$sec"/>
+                  </a>
+                </xsl:when>
+                <xsl:otherwise><xsl:value-of select="$secterm"/><xsl:text> </xsl:text><xsl:value-of select="$sec"/></xsl:otherwise>
+              </xsl:choose>
+              <xsl:if test="$sfmt='of'">
+                <xsl:text> of </xsl:text>
+              </xsl:if>
+            </xsl:when>
+            <xsl:when test="$sfmt='number-only'">
+              <xsl:choose>
+                <xsl:when test="$href!=''">
+                  <a href="{$href}">
+                    <xsl:if test="$title!=''">
+                      <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+                    </xsl:if>
+                    <xsl:if test="$xml2rfc-ext-include-references-in-index='yes'">
+                      <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+                    </xsl:if>
+                    <xsl:value-of select="$sec"/>
+                  </a>
+                </xsl:when>
+                <xsl:otherwise><xsl:value-of select="$sec"/></xsl:otherwise>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise />
+          </xsl:choose>
+        </xsl:if>
+
+        <xsl:if test="$sec='' or ($sfmt!='section' and $sfmt!='number-only')">
+          <xsl:choose>
+            <xsl:when test="$xref/@format='none'">
+              <!-- Nothing to do -->
+            </xsl:when>
+            <xsl:otherwise>
+              <a href="#{$xref/@target}">
+                <xsl:if test="$xml2rfc-ext-include-references-in-index='yes'">
+                  <xsl:attribute name="id"><xsl:value-of select="$anchor"/></xsl:attribute>
+                </xsl:if>
+                <cite title="{normalize-space($node/front/title)}">
+                  <xsl:variable name="val">
+                    <xsl:call-template name="reference-name">
+                      <xsl:with-param name="node" select="$node" />
+                    </xsl:call-template>
+                  </xsl:variable>
+                  <xsl:choose>
+                    <xsl:when test="$xref/@format='counter'">
+                      <!-- remove brackets -->
+                      <xsl:value-of select="substring($val,2,string-length($val)-2)"/>
+                    </xsl:when>
+                    <xsl:when test="$xref/@format='title'">
+                      <xsl:value-of select="$node/front/title"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      <xsl:value-of select="$val"/>
+                    </xsl:otherwise>
+                  </xsl:choose>
+                </cite>
+              </a>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+
+        <xsl:if test="$sec!=''">
+          <xsl:choose>
+            <xsl:when test="$sfmt='parens'">
+              <xsl:text> (</xsl:text>
+              <xsl:choose>
+                <xsl:when test="$href!=''">
+                  <a href="{$href}"><xsl:value-of select="$secterm"/><xsl:text> </xsl:text><xsl:value-of select="$sec"/></a>
+                </xsl:when>
+                <xsl:otherwise><xsl:value-of select="$secterm"/><xsl:text> </xsl:text><xsl:value-of select="$sec"/></xsl:otherwise>
+              </xsl:choose>
+              <xsl:text>)</xsl:text>
+            </xsl:when>
+            <xsl:when test="$sfmt='comma'">
+              <xsl:text>, </xsl:text>
+              <xsl:choose>
+                <xsl:when test="$href!=''">
+                  <a href="{$href}">
+                    <xsl:if test="$title!=''">
+                      <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+                    </xsl:if>
+                    <xsl:value-of select="$secterm"/>
+                    <xsl:text> </xsl:text>
+                    <xsl:value-of select="$sec"/>
+                  </a>
+                </xsl:when>
+                <xsl:otherwise><xsl:value-of select="$secterm"/><xsl:text> </xsl:text><xsl:value-of select="$sec"/></xsl:otherwise>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </xsl:if>
+      </xsl:when>
+
+      <xsl:otherwise>
+        <xsl:if test="$node">
+          <!-- make it the correct context -->
+          <xsl:for-each select="$xref">
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="concat('xref to unknown element: ',name($node))"/>
+            </xsl:call-template>
+          </xsl:for-each>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+
+<!-- mark unmatched elements red -->
+
+<xsl:template match="*">
+  <xsl:call-template name="error">
+    <xsl:with-param name="inline" select="'no'"/>
+    <xsl:with-param name="msg">no XSLT template for element '<xsl:value-of select="name()"/>'</xsl:with-param>
+  </xsl:call-template>
+  <span class="tt {$css-error}">&lt;<xsl:value-of select="name()" />&gt;</span>
+  <xsl:copy><xsl:apply-templates select="node()|@*" /></xsl:copy>
+  <span class="tt {$css-error}">&lt;/<xsl:value-of select="name()" />&gt;</span>
+</xsl:template>
+
+<xsl:template match="/">
+  <xsl:apply-templates select="*" mode="validate"/>
+  <xsl:apply-templates select="*" />
+</xsl:template>
+
+<!-- utility templates -->
+
+<xsl:template name="collectLeftHeaderColumn">
+  <!-- default case -->
+  <xsl:if test="$xml2rfc-private=''">
+    <xsl:choose>
+      <xsl:when test="/rfc/@number and $header-format='2010' and $submissionType='independent'">
+        <myns:item>Independent Submission</myns:item>
+      </xsl:when>
+      <xsl:when test="/rfc/@number and $header-format='2010' and $submissionType='IETF'">
+        <myns:item>Internet Engineering Task Force (IETF)</myns:item>
+      </xsl:when>
+      <xsl:when test="/rfc/@number and $header-format='2010' and $submissionType='IRTF'">
+        <myns:item>Internet Research Task Force (IRTF)</myns:item>
+      </xsl:when>
+      <xsl:when test="/rfc/@number and $header-format='2010' and $submissionType='IAB'">
+        <myns:item>Internet Architecture Board (IAB)</myns:item>
+      </xsl:when>
+      <xsl:when test="/rfc/front/workgroup and (not(/rfc/@number) or /rfc/@number='')">
+        <xsl:choose>
+          <xsl:when test="starts-with(/rfc/@docName,'draft-ietf-') and $submissionType='IETF'"/>
+          <xsl:when test="starts-with(/rfc/@docName,'draft-irft-') and $submissionType='IRTF'"/>
+          <xsl:otherwise>
+            <xsl:call-template name="info">
+              <xsl:with-param name="msg">The /rfc/front/workgroup should only be used for Working/Research Group drafts</xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:for-each select="/rfc/front/workgroup">
+          <xsl:variable name="v" select="normalize-space(.)"/>
+          <xsl:variable name="tmp" select="translate($v, $ucase, $lcase)"/>
+          <xsl:if test="contains($tmp,' research group') or contains($tmp,' working group')">
+            <xsl:call-template name="info">
+              <xsl:with-param name="msg">No need to include 'Working Group' or 'Research Group' postfix in /rfc/front/workgroup value '<xsl:value-of select="$v"/>'</xsl:with-param>
+            </xsl:call-template>
+          </xsl:if>
+          <xsl:variable name="h">
+            <!-- when a single name, append WG/RG postfix automatically -->
+            <xsl:choose>
+              <xsl:when test="not(contains($v, ' ')) and starts-with(/rfc/@docName,'draft-ietf-') and $submissionType='IETF'">
+                <xsl:value-of select="concat($v, ' Working Group')"/>
+              </xsl:when>
+              <xsl:when test="not(contains($v, ' ')) and starts-with(/rfc/@docName,'draft-irtf-') and $submissionType='IRTF'">
+                <xsl:value-of select="concat($v, ' Research Group')"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$v"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+          <myns:item>
+            <xsl:value-of select="$h"/>
+          </myns:item>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:if test="starts-with(/rfc/@docName,'draft-ietf-') and not(/rfc/front/workgroup)">
+          <xsl:call-template name="info">
+            <xsl:with-param name="msg">WG submissions should include a /rfc/front/workgroup element</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <myns:item>Network Working Group</myns:item>
+      </xsl:otherwise>
+    </xsl:choose>
+    <!-- check <area> value -->
+    <xsl:for-each select="/rfc/front/area">
+      <xsl:variable name="area" select="normalize-space(.)"/>
+      <xsl:variable name="rallowed">
+        <xsl:if test="$pub-yearmonth &lt; 201509">
+          <ed:v>Applications</ed:v>
+          <ed:v>app</ed:v>
+        </xsl:if>
+        <xsl:if test="$pub-yearmonth &gt; 201505">
+          <ed:v>Applications and Real-Time</ed:v>
+          <ed:v>art</ed:v>
+        </xsl:if>
+        <ed:v>General</ed:v>
+        <ed:v>gen</ed:v>
+        <ed:v>Internet</ed:v>
+        <ed:v>int</ed:v>
+        <ed:v>Operations and Management</ed:v>
+        <ed:v>ops</ed:v>
+        <xsl:if test="$pub-yearmonth &lt; 201509">
+          <ed:v>Real-time Applications and Infrastructure</ed:v>
+          <ed:v>rai</ed:v>
+        </xsl:if>
+        <ed:v>Routing</ed:v>
+        <ed:v>rtg</ed:v>
+        <ed:v>Security</ed:v>
+        <ed:v>sec</ed:v>
+        <ed:v>Transport</ed:v>
+        <ed:v>tsv</ed:v>
+      </xsl:variable>
+      <xsl:variable name="allowed" select="exslt:node-set($rallowed)"/>
+      <xsl:choose>
+        <xsl:when test="$allowed/ed:v=$area">
+          <!-- ok -->
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">Unknown IETF area: "<xsl:value-of select="$area"/>" - should be one of: <xsl:for-each select="$allowed/ed:v">
+              <xsl:text>"</xsl:text>
+              <xsl:value-of select="."/>
+              <xsl:text>"</xsl:text>
+              <xsl:if test="position()!=last()">
+                <xsl:text>, </xsl:text>
+              </xsl:if>
+            </xsl:for-each>
+            <xsl:text> (as of the publication date of </xsl:text>
+            <xsl:value-of select="$pub-yearmonth"/>
+            <xsl:text>)</xsl:text>
+            </xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+    <myns:item>
+       <xsl:choose>
+        <xsl:when test="/rfc/@ipr and not(/rfc/@number)">Internet-Draft</xsl:when>
+        <xsl:otherwise>Request for Comments: <xsl:value-of select="/rfc/@number"/></xsl:otherwise>
+      </xsl:choose>
+    </myns:item>
+    <xsl:if test="/rfc/@obsoletes!=''">
+      <myns:item>
+        <xsl:text>Obsoletes: </xsl:text>
+        <xsl:call-template name="rfclist">
+          <xsl:with-param name="list" select="normalize-space(/rfc/@obsoletes)" />
+        </xsl:call-template>
+        <xsl:if test="not(/rfc/@number)"> (if approved)</xsl:if>
+      </myns:item>
+    </xsl:if>
+    <xsl:if test="/rfc/@seriesNo">
+       <myns:item>
+        <xsl:choose>
+          <xsl:when test="/rfc/@category='bcp'">BCP: <xsl:value-of select="/rfc/@seriesNo" /></xsl:when>
+          <xsl:when test="/rfc/@category='info'">FYI: <xsl:value-of select="/rfc/@seriesNo" /></xsl:when>
+          <xsl:when test="/rfc/@category='std'">STD: <xsl:value-of select="/rfc/@seriesNo" /></xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="warning">
+              <xsl:with-param name="msg">There is no IETF document series called '<xsl:value-of select="/rfc/@category"/>'</xsl:with-param>
+            </xsl:call-template>
+            <xsl:value-of select="concat(translate(/rfc/@category,$lcase,$ucase),': ',/rfc/@seriesNo)" />
+          </xsl:otherwise>
+        </xsl:choose>
+      </myns:item>
+    </xsl:if>
+    <xsl:if test="/rfc/@updates!=''">
+      <myns:item>
+        <xsl:text>Updates: </xsl:text>
+          <xsl:call-template name="rfclist">
+             <xsl:with-param name="list" select="normalize-space(/rfc/@updates)" />
+          </xsl:call-template>
+          <xsl:if test="not(/rfc/@number)"> (if approved)</xsl:if>
+      </myns:item>
+    </xsl:if>
+    <myns:item>
+      <xsl:choose>
+        <xsl:when test="/rfc/@number">
+          <xsl:text>Category: </xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:text>Intended status: </xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:call-template name="get-category-long" />
+    </myns:item>
+    <xsl:if test="/rfc/@ipr and not(/rfc/@number)">
+       <myns:item>Expires: <xsl:call-template name="expirydate" /></myns:item>
+    </xsl:if>
+  </xsl:if>
+
+  <!-- private case -->
+  <xsl:if test="$xml2rfc-private!=''">
+    <myns:item><xsl:value-of select="$xml2rfc-private" /></myns:item>
+  </xsl:if>
+
+  <xsl:if test="$header-format='2010' and /rfc/@number">
+    <myns:item>ISSN: 2070-1721</myns:item>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="collectRightHeaderColumn">
+  <xsl:for-each select="author">
+    <xsl:variable name="initials">
+      <xsl:call-template name="format-initials"/>
+    </xsl:variable>
+    <xsl:variable name="truncated-initials">
+      <xsl:call-template name="truncate-initials">
+        <xsl:with-param name="initials" select="$initials"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:if test="@surname">
+      <myns:item>
+        <xsl:value-of select="$truncated-initials"/>
+        <xsl:if test="$truncated-initials!=''">
+          <xsl:text> </xsl:text>
+        </xsl:if>
+        <xsl:value-of select="@surname" />
+        <xsl:if test="@asciiInitials!='' or @asciiSurname!=''">
+          <xsl:text> (</xsl:text>
+            <xsl:value-of select="@asciiInitials"/>
+            <xsl:if test="@asciiInitials!='' and @asciiSurname!=''"> </xsl:if>
+            <xsl:value-of select="@asciiSurname"/>
+          <xsl:text>)</xsl:text>
+        </xsl:if>
+        <xsl:if test="@role">
+          <xsl:choose>
+            <xsl:when test="@role='editor'">
+              <xsl:text>, Editor</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:text>, </xsl:text><xsl:value-of select="@role" />
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+      </myns:item>
+    </xsl:if>
+    <xsl:variable name="org">
+      <xsl:choose>
+        <xsl:when test="organization/@abbrev"><xsl:value-of select="organization/@abbrev" /></xsl:when>
+        <xsl:otherwise><xsl:value-of select="organization" /></xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="orgOfFollowing">
+      <xsl:choose>
+        <xsl:when test="following-sibling::*[1]/organization/@abbrev"><xsl:value-of select="following-sibling::*[1]/organization/@abbrev" /></xsl:when>
+        <xsl:otherwise><xsl:value-of select="following-sibling::*/organization" /></xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$org != $orgOfFollowing and $org != ''">
+      <myns:item>
+        <xsl:value-of select="$org"/>
+        <xsl:if test="organization/@ascii">
+          <xsl:value-of select="concat(' (',organization/@ascii,')')"/>
+        </xsl:if>
+      </myns:item>
+    </xsl:if>
+  </xsl:for-each>
+  <myns:item>
+    <xsl:if test="$xml2rfc-ext-pub-month!=''">
+      <xsl:value-of select="$xml2rfc-ext-pub-month" />
+      <xsl:if test="$xml2rfc-ext-pub-day!='' and /rfc/@ipr and not(/rfc/@number)">
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="number($xml2rfc-ext-pub-day)" />
+        <xsl:text>,</xsl:text>
+      </xsl:if>
+    </xsl:if>
+    <xsl:if test="$xml2rfc-ext-pub-month='' and $rfcno!=''">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'month missing but is required for RFCs'"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$xml2rfc-ext-pub-day='' and /rfc/@docName and not(substring(/rfc/@docName, string-length(/rfc/@docName) - string-length('-latest') + 1) = '-latest')">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('/rfc/front/date/@day appears to be missing for a historic draft dated ', $pub-yearmonth)"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:value-of select="concat(' ',$xml2rfc-ext-pub-year)" />
+  </myns:item>
+</xsl:template>
+
+
+<xsl:template name="emitheader">
+  <xsl:param name="lc" />
+  <xsl:param name="rc" />
+
+  <tbody>
+    <xsl:for-each select="$lc/myns:item | $rc/myns:item">
+      <xsl:variable name="pos" select="position()" />
+      <xsl:if test="$pos &lt; count($lc/myns:item) + 1 or $pos &lt; count($rc/myns:item) + 1">
+        <tr>
+          <td class="{$css-left}"><xsl:call-template name="copynodes"><xsl:with-param name="nodes" select="$lc/myns:item[$pos]/node()" /></xsl:call-template></td>
+          <td class="{$css-right}"><xsl:call-template name="copynodes"><xsl:with-param name="nodes" select="$rc/myns:item[$pos]/node()" /></xsl:call-template></td>
+        </tr>
+      </xsl:if>
+    </xsl:for-each>
+  </tbody>
+</xsl:template>
+
+<!-- convenience template that avoids copying namespace nodes we don't want -->
+<xsl:template name="copynodes">
+  <xsl:param name="nodes" />
+  <xsl:for-each select="$nodes">
+    <xsl:choose>
+      <xsl:when test="namespace-uri()='http://www.w3.org/1999/xhtml'">
+        <xsl:element name="{name()}" namespace="{namespace-uri()}">
+          <xsl:copy-of select="@*|node()" />
+        </xsl:element>
+      </xsl:when>
+      <xsl:when test="self::*">
+        <xsl:element name="{name()}">
+          <xsl:copy-of select="@*|node()" />
+        </xsl:element>
+      </xsl:when>
+      <!-- workaround for opera, remove when Opera > 9.0.x comes out -->
+      <xsl:when test="self::text()">
+        <xsl:value-of select="."/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:copy-of select="." />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="expirydate">
+  <xsl:param name="in-prose"/>
+  <xsl:choose>
+    <xsl:when test="number($xml2rfc-ext-pub-day) >= 1">
+      <!-- have day of month? -->
+      <xsl:if test="$in-prose">
+        <xsl:text>on </xsl:text>
+      </xsl:if>
+      <xsl:call-template name="normalize-date">
+        <xsl:with-param name="year" select="$xml2rfc-ext-pub-year"/>
+        <xsl:with-param name="month" select="$pub-month-numeric"/>
+        <xsl:with-param name="day" select="$xml2rfc-ext-pub-day + 185"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:if test="$in-prose">
+        <xsl:text>in </xsl:text>
+      </xsl:if>
+      <xsl:variable name="month">
+        <xsl:call-template name="get-month-as-num">
+          <xsl:with-param name="month" select="$xml2rfc-ext-pub-month"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="string(number($month))!='NaN' and number($month) &gt; 0 and number($month) &lt; 7">
+          <xsl:call-template name="get-month-as-name">
+            <xsl:with-param name="month" select="number($month) + 6"/>
+          </xsl:call-template>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-ext-pub-year" />
+        </xsl:when>
+        <xsl:when test="string(number($month))!='NaN' and number($month) &gt; 6 and number($month) &lt; 13">
+          <xsl:call-template name="get-month-as-name">
+            <xsl:with-param name="month" select="number($month) - 6"/>
+          </xsl:call-template>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-ext-pub-year + 1" />
+        </xsl:when>
+        <xsl:otherwise>WRONG SYNTAX FOR MONTH</xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="normalize-date">
+  <xsl:param name="year"/>
+  <xsl:param name="month"/>
+  <xsl:param name="day"/>
+
+  <xsl:variable name="isleap" select="(($year mod 4) = 0 and ($year mod 100 != 0)) or ($year mod 400) = 0" />
+
+  <!--<xsl:message>
+    <xsl:value-of select="concat($year,' ',$month,' ',$day)"/>
+  </xsl:message>-->
+
+  <xsl:variable name="dim">
+    <xsl:choose>
+      <xsl:when test="$month=1 or $month=3 or $month=5 or $month=7 or $month=8 or $month=10 or $month=12">31</xsl:when>
+      <xsl:when test="$month=2 and $isleap">29</xsl:when>
+      <xsl:when test="$month=2 and not($isleap)">28</xsl:when>
+      <xsl:otherwise>30</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$day > $dim and $month=12">
+      <xsl:call-template name="normalize-date">
+        <xsl:with-param name="year" select="$year + 1"/>
+        <xsl:with-param name="month" select="1"/>
+        <xsl:with-param name="day" select="$day - $dim"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="$day > $dim">
+      <xsl:call-template name="normalize-date">
+        <xsl:with-param name="year" select="$year"/>
+        <xsl:with-param name="month" select="$month + 1"/>
+        <xsl:with-param name="day" select="$day - $dim"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="get-month-as-name">
+        <xsl:with-param name="month" select="$month"/>
+      </xsl:call-template>
+      <xsl:value-of select="concat(' ',$day,', ',$year)"/>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="get-month-as-num">
+  <xsl:param name="month" />
+  <xsl:choose>
+    <xsl:when test="$month='January'">01</xsl:when>
+    <xsl:when test="$month='February'">02</xsl:when>
+    <xsl:when test="$month='March'">03</xsl:when>
+    <xsl:when test="$month='April'">04</xsl:when>
+    <xsl:when test="$month='May'">05</xsl:when>
+    <xsl:when test="$month='June'">06</xsl:when>
+    <xsl:when test="$month='July'">07</xsl:when>
+    <xsl:when test="$month='August'">08</xsl:when>
+    <xsl:when test="$month='September'">09</xsl:when>
+    <xsl:when test="$month='October'">10</xsl:when>
+    <xsl:when test="$month='November'">11</xsl:when>
+    <xsl:when test="$month='December'">12</xsl:when>
+    <xsl:otherwise>WRONG SYNTAX FOR MONTH</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-month-as-name">
+  <xsl:param name="month"/>
+  <xsl:choose>
+    <xsl:when test="$month=1">January</xsl:when>
+    <xsl:when test="$month=2">February</xsl:when>
+    <xsl:when test="$month=3">March</xsl:when>
+    <xsl:when test="$month=4">April</xsl:when>
+    <xsl:when test="$month=5">May</xsl:when>
+    <xsl:when test="$month=6">June</xsl:when>
+    <xsl:when test="$month=7">July</xsl:when>
+    <xsl:when test="$month=8">August</xsl:when>
+    <xsl:when test="$month=9">September</xsl:when>
+    <xsl:when test="$month=10">October</xsl:when>
+    <xsl:when test="$month=11">November</xsl:when>
+    <xsl:when test="$month=12">December</xsl:when>
+    <xsl:otherwise>WRONG SYNTAX FOR MONTH</xsl:otherwise>
+   </xsl:choose>
+</xsl:template>
+
+<!-- produce back section with author information -->
+<xsl:template name="get-authors-section-title">
+  <xsl:choose>
+    <xsl:when test="count(/rfc/front/author)=1">Author's Address</xsl:when>
+    <xsl:otherwise>Authors' Addresses</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-authors-section-number">
+  <xsl:if test="/*/x:assign-section-number[@builtin-target='authors']">
+    <xsl:value-of select="/*/x:assign-section-number[@builtin-target='authors']/@number"/>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insertAuthors">
+
+  <xsl:variable name="number">
+    <xsl:call-template name="get-authors-section-number"/>
+  </xsl:variable>
+
+  <xsl:if test="$number!='suppress' and $xml2rfc-authorship!='no'">
+    <xsl:call-template name="insert-conditional-hrule"/>
+
+    <section id="{$anchor-pref}authors" class="avoidbreakinside">
+      <xsl:call-template name="insert-conditional-pagebreak"/>
+      <h2>
+        <xsl:if test="$number != ''">
+          <a href="#{$anchor-pref}section.{$number}" id="{$anchor-pref}section.{$number}"><xsl:value-of select="$number"/>.</a>
+          <xsl:text> </xsl:text>
+        </xsl:if>
+        <a href="#{$anchor-pref}authors"><xsl:call-template name="get-authors-section-title"/></a>
+      </h2>
+
+      <xsl:apply-templates select="/rfc/front/author" />
+    </section>
+  </xsl:if>
+</xsl:template>
+
+
+
+<!-- insert copyright statement -->
+
+<xsl:template name="insertCopyright" myns:namespaceless-elements="xml2rfc">
+
+<boilerplate>
+  <xsl:if test="not($no-copylong)">
+    <section title="Full Copyright Statement" anchor="{$anchor-pref}copyright" x:fixed-section-number="3">
+      <xsl:choose>
+        <xsl:when test="$ipr-rfc3667">
+          <t>
+            <xsl:choose>
+              <xsl:when test="$ipr-rfc4748">
+                Copyright &#169; The IETF Trust (<xsl:value-of select="$xml2rfc-ext-pub-year" />).
+              </xsl:when>
+              <xsl:otherwise>
+                Copyright &#169; The Internet Society (<xsl:value-of select="$xml2rfc-ext-pub-year" />).
+              </xsl:otherwise>
+            </xsl:choose>
+          </t>
+          <t>
+            This document is subject to the rights, licenses and restrictions
+            contained in BCP 78<xsl:if test="$submissionType='independent'"> and at <eref target="http://www.rfc-editor.org/copyright.html">http://www.rfc-editor.org/copyright.html</eref></xsl:if>, and except as set forth therein, the authors
+            retain all their rights.
+          </t>
+          <t>
+            This document and the information contained herein are provided
+            on an &#8220;AS IS&#8221; basis and THE CONTRIBUTOR,
+            THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY),
+            THE INTERNET SOCIETY<xsl:if test="$ipr-rfc4748">, THE IETF TRUST</xsl:if>
+            AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES,
+            EXPRESS OR IMPLIED,
+            INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+            INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+            WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+          </t>
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- <http://tools.ietf.org/html/rfc2026#section-10.4> -->
+          <t>
+            Copyright &#169; The Internet Society (<xsl:value-of select="$xml2rfc-ext-pub-year" />). All Rights Reserved.
+          </t>
+          <t>
+            This document and translations of it may be copied and furnished to
+            others, and derivative works that comment on or otherwise explain it
+            or assist in its implementation may be prepared, copied, published and
+            distributed, in whole or in part, without restriction of any kind,
+            provided that the above copyright notice and this paragraph are
+            included on all such copies and derivative works. However, this
+            document itself may not be modified in any way, such as by removing
+            the copyright notice or references to the Internet Society or other
+            Internet organizations, except as needed for the purpose of
+            developing Internet standards in which case the procedures for
+            copyrights defined in the Internet Standards process must be
+            followed, or as required to translate it into languages other than
+            English.
+          </t>
+          <t>
+            The limited permissions granted above are perpetual and will not be
+            revoked by the Internet Society or its successors or assigns.
+          </t>
+          <t>
+            This document and the information contained herein is provided on an
+            &#8220;AS IS&#8221; basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+            TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+            BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+            HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+            MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+          </t>
+        </xsl:otherwise>
+      </xsl:choose>
+    </section>
+
+    <section title="Intellectual Property" anchor="{$anchor-pref}ipr" x:fixed-section-number="4">
+      <xsl:choose>
+        <xsl:when test="$ipr-rfc3667">
+          <t>
+            The IETF takes no position regarding the validity or scope of any
+            Intellectual Property Rights or other rights that might be claimed to
+            pertain to the implementation or use of the technology described in
+            this document or the extent to which any license under such rights
+            might or might not be available; nor does it represent that it has
+            made any independent effort to identify any such rights.  Information
+            on the procedures with respect to rights in RFC documents
+            can be found in BCP 78 and BCP 79.
+          </t>
+          <t>
+            Copies of IPR disclosures made to the IETF Secretariat and any
+            assurances of licenses to be made available, or the result of an
+            attempt made to obtain a general license or permission for the use
+            of such proprietary rights by implementers or users of this
+            specification can be obtained from the IETF on-line IPR repository
+            at <eref target="http://www.ietf.org/ipr">http://www.ietf.org/ipr</eref>.
+          </t>
+          <t>
+            The IETF invites any interested party to bring to its attention any
+            copyrights, patents or patent applications, or other proprietary
+            rights that may cover technology that may be required to implement
+            this standard. Please address the information to the IETF at
+            <eref target="mailto:ietf-ipr@ietf.org">ietf-ipr@ietf.org</eref>.
+          </t>
+        </xsl:when>
+        <xsl:otherwise>
+          <t>
+            The IETF takes no position regarding the validity or scope of
+            any intellectual property or other rights that might be claimed
+            to  pertain to the implementation or use of the technology
+            described in this document or the extent to which any license
+            under such rights might or might not be available; neither does
+            it represent that it has made any effort to identify any such
+            rights. Information on the IETF's procedures with respect to
+            rights in standards-track and standards-related documentation
+            can be found in BCP-11. Copies of claims of rights made
+            available for publication and any assurances of licenses to
+            be made available, or the result of an attempt made
+            to obtain a general license or permission for the use of such
+            proprietary rights by implementors or users of this
+            specification can be obtained from the IETF Secretariat.
+          </t>
+          <t>
+            The IETF invites any interested party to bring to its
+            attention any copyrights, patents or patent applications, or
+            other proprietary rights which may cover technology that may be
+            required to practice this standard. Please address the
+            information to the IETF Executive Director.
+          </t>
+          <xsl:if test="$xml2rfc-iprnotified='yes'">
+            <t>
+              The IETF has been notified of intellectual property rights
+              claimed in regard to some or all of the specification contained
+              in this document. For more information consult the online list
+              of claimed rights.
+            </t>
+          </xsl:if>
+        </xsl:otherwise>
+      </xsl:choose>
+    </section>
+
+    <xsl:choose>
+      <xsl:when test="$no-funding"/>
+      <xsl:when test="$funding1 and /rfc/@number">
+        <section x:fixed-section-number="5">
+          <xsl:attribute name="title">
+            <xsl:choose>
+              <xsl:when test="$xml2rfc-rfcedstyle='yes'">Acknowledgement</xsl:when>
+              <xsl:otherwise>Acknowledgment</xsl:otherwise>
+            </xsl:choose>
+          </xsl:attribute>
+          <t>
+            Funding for the RFC Editor function is provided by the IETF
+            Administrative Support Activity (IASA).
+          </t>
+        </section>
+      </xsl:when>
+      <xsl:when test="$funding0 and /rfc/@number">
+        <section x:fixed-section-number="5">
+          <xsl:attribute name="title">
+            <xsl:choose>
+              <xsl:when test="$xml2rfc-rfcedstyle='yes'">Acknowledgement</xsl:when>
+              <xsl:otherwise>Acknowledgment</xsl:otherwise>
+            </xsl:choose>
+          </xsl:attribute>
+          <t>
+            Funding for the RFC Editor function is currently provided by
+            the Internet Society.
+          </t>
+        </section>
+      </xsl:when>
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:if>
+</boilerplate>
+</xsl:template>
+
+<!-- optional scripts -->
+<xsl:template name="insertScripts">
+<xsl:if test="$xml2rfc-ext-refresh-from!=''">
+<script type="application/javascript">
+var RfcRefresh = {};
+RfcRefresh.NS_XHTML = "http://www.w3.org/1999/xhtml";
+RfcRefresh.NS_MOZERR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
+RfcRefresh.lastTxt = "";
+RfcRefresh.lastEtag = "";
+RfcRefresh.xslt = null;
+RfcRefresh.xmlsource = "<xsl:value-of select='$xml2rfc-ext-refresh-from'/>";
+RfcRefresh.xsltsource = "<xsl:value-of select='$xml2rfc-ext-refresh-xslt'/>";
+RfcRefresh.interval = "<xsl:value-of select='number($xml2rfc-ext-refresh-interval)'/>";
+
+RfcRefresh.getXSLT = function() {
+  if (! window.XSLTProcessor) {
+    var err = document.createElement("pre");
+    err.className = "refreshbrowsererror <xsl:value-of select="$css-noprint"/>";
+    var msg = "This browser does not support the window.XSLTProcessor functionality.";
+    err.appendChild(document.createTextNode(msg));
+    RfcRefresh.showMessage("refreshxmlerror", err);
+  }
+  else {
+    try {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", RfcRefresh.xsltsource, true);
+      xhr.onload = function (e) {
+        if (xhr.readyState === 4) {
+          RfcRefresh.xslt = new XSLTProcessor();
+          RfcRefresh.xslt.importStylesheet(xhr.responseXML);
+        }
+      }
+      xhr.onerror = function (e) {
+        console.error(xhr.status + " " + xhr.statusText);
+      };
+      xhr.send(null);
+    }
+    catch (e) {
+      var err = document.createElement("pre");
+      err.className = "refreshbrowsererror <xsl:value-of select="$css-noprint"/>";
+      var msg = "Failed to load XSLT code from &lt;" + RfcRefresh.xsltsource + "&gt;.\n";
+      msg += "Your browser might not support loading from a file: URI.\n";
+      msg += "Error details: " + e;
+      err.appendChild(document.createTextNode(msg));
+      RfcRefresh.showMessage("refreshxmlerror", err);
+    }
+  }
+}
+
+RfcRefresh.findAndUpdate = function(olddoc, elem) {
+  var changed = "";
+  var children = elem.childNodes;
+  for (var i = 0; i != children.length; i++) {
+    var n = children[i];
+    if (n.nodeType == 1) {
+      var c = RfcRefresh.findAndUpdate(olddoc, n);
+      if (changed == '') {
+        changed = c;
+      }
+      var id = n.id;
+      if (id != "") {
+        var old = olddoc.getElementById(id);
+        var newtext = n.innerHTML;
+        if (!old) {
+          console.debug("new " + id);
+        } else {
+          var oldtext = old.innerHTML;
+          if (oldtext != newtext) {
+            console.debug("updating " + id);
+            old.innerHTML = n.innerHTML;
+            if (changed == '') {
+              changed = id;
+            }
+          }
+        }
+      }
+    }
+  }
+  return changed;
+}
+
+RfcRefresh.findDifferences = function(olddoc, newdoc) {
+  var changed = RfcRefresh.findAndUpdate(olddoc, newdoc.documentElement);
+  if (changed != "") {
+    console.debug("changed: " + changed);
+    document.location = "#" + changed;
+  }
+  // final check for changes; if those were not processed earlier,
+  // we refresh the whole document
+  var oldtext = olddoc.documentElement.getElementsByTagName("body")[0].innerHTML;
+  var newtext = newdoc.documentElement.getElementsByTagName("body")[0].innerHTML;
+  if (oldtext != newtext) {
+    console.debug("full refresh: " + newtext);
+    olddoc.documentElement.innerHTML = newdoc.documentElement.innerHTML;
+  }
+}
+
+RfcRefresh.getNodeText = function(elem) {
+  var result = "";
+  var children = elem.childNodes;
+  for (var i = 0; i != children.length; i++) {
+    if (children[i].nodeType == 3) {
+      result += children[i].nodeValue;
+    }
+  }
+  return result; 
+}
+
+RfcRefresh.getParserError = function(dom) {
+  // FIREFOX
+  if ("parsererror" == dom.documentElement.nodeName &amp;&amp; RfcRefresh.NS_MOZERR == dom.documentElement.namespaceURI) {
+    var errmsg = new Object();
+    errmsg.msg = "";
+    errmsg.src = "";
+    var children = dom.documentElement.childNodes;
+    for (var i = 0; i != children.length; i++) {
+      if (children[i].nodeType == 3) {
+        errmsg.msg += children[i].nodeValue;
+      } else if (children[i].nodeName == "sourcetext") {
+        errmsg.src = RfcRefresh.getNodeText(children[i]);
+      }
+    }
+    return errmsg;
+  }
+  
+  var list = dom.getElementsByTagNameNS(RfcRefresh.NS_XHTML, "parsererror");
+  if (list.length != 0) {
+    // Webkit
+    var errmsg = new Object();
+    errmsg.msg = "XML parse error";
+    list = dom.getElementsByTagNameNS(RfcRefresh.NS_XHTML, "div");
+    if (list.length != 0) {
+      errmsg.msg = RfcRefresh.getNodeText(list[0]);
+    }
+    return errmsg;
+  }  
+  
+  
+  return null;
+}
+
+RfcRefresh.showMessage = function(cls, node) {
+  // remove previous message
+  var list = document.getElementsByClassName(cls);
+  if (list.length != 0) {
+    list[0].parentNode.removeChild(list[0]);
+  }
+  document.body.appendChild(node);
+}
+
+RfcRefresh.refresh = function(txt) {
+  if (txt != RfcRefresh.lastTxt) {
+    RfcRefresh.lastTxt = txt;
+    // try to parse
+    var parser = new DOMParser();
+    var dom = parser.parseFromString(txt, "text/xml");
+    var errmsg = RfcRefresh.getParserError(dom);
+    
+    if (errmsg != null) {
+      var err = document.createElement("pre");
+      err.className = "refreshxmlerror <xsl:value-of select="$css-noprint"/>";
+      err.appendChild(document.createTextNode(errmsg.msg));
+      if (errmsg.src != null) {
+        err.appendChild(document.createElement("hr"));
+        err.appendChild(document.createTextNode(errmsg.src));
+      }
+      RfcRefresh.showMessage("refreshxmlerror", err);
+    } else {
+      // find new refresh
+      var children = dom.childNodes;
+      for (var i = 0; i != children.length; i++) {
+        if (children[i].nodeType == 7 &amp;&amp; children[i].target == "rfc-ext") {
+          var s = "&lt;foo " + children[i].data + "/>";
+          var sd = parser.parseFromString(s, "text/xml");
+          var refresh = sd.documentElement.getAttribute("refresh-interval");
+          if (refresh != null &amp;&amp; refresh != "") {
+            refresh = parseInt(refresh, 10);
+            if (RfcRefresh.interval != refresh) {
+              if (Number.isNaN(refresh) || refresh &lt; 5) {
+                console.debug("refresh requested to be: " + refresh + " - ignored, using 5 instead.");
+                RfcRefresh.interval = 5;
+              } else {
+                RfcRefresh.interval = refresh;
+                console.debug("refresh changed to: " + refresh);
+              }
+            }
+          }
+        }
+      }
+    
+      var html = RfcRefresh.xslt.transformToDocument(dom);
+      RfcRefresh.findDifferences(document, html);
+    }
+  }
+}
+
+RfcRefresh.initRefresh = function() {
+  RfcRefresh.getXSLT();
+    
+  window.setTimeout(function(){
+    if (RfcRefresh.xslt != null) {
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", RfcRefresh.xmlsource, true);
+      if (RfcRefresh.lastEtag != "") {
+        xhr.setRequestHeader("If-None-Match", RfcRefresh.lastEtag);
+      }
+      xhr.onload = function (e) {
+        if (xhr.readyState === 4) {
+          console.debug(xhr.status + " " + xhr.statusText);
+          if (xhr.status != 304) {
+            RfcRefresh.refresh(xhr.responseText);
+          }
+          RfcRefresh.lastEtag = xhr.getResponseHeader("ETag");
+        }
+      }
+      xhr.onerror = function (e) {
+        console.error(xhr.status + " " + xhr.statusText);
+      };
+      xhr.send(null);
+      setTimeout(arguments.callee, RfcRefresh.interval * 1000);
+    }
+  }, RfcRefresh.interval * 1000);
+}
+</script>
+</xsl:if>
+<xsl:if test="/rfc/x:feedback">
+<script type="application/javascript">
+var buttonsAdded = false;
+
+function initFeedback() {
+  var fb = document.createElement("div");
+  fb.className = "<xsl:value-of select="concat($css-feedback,' ',$css-noprint)"/>";
+  fb.setAttribute("onclick", "feedback();");
+  fb.appendChild(document.createTextNode("feedback"));
+
+  document.body.appendChild(fb);
+}
+
+function feedback() {
+  toggleButtonsToElementsByName("h2");
+  toggleButtonsToElementsByName("h3");
+  toggleButtonsToElementsByName("h4");
+  toggleButtonsToElementsByName("h5");
+
+  buttonsAdded = !buttonsAdded;
+}
+
+function toggleButtonsToElementsByName(name) {
+  var list = document.getElementsByTagName(name);
+  for (var i = 0; i &lt; list.length; i++) {
+    toggleButton(list.item(i));
+  }
+}
+
+function toggleButton(node) {
+  if (! buttonsAdded) {
+
+    // docname
+    var template = "<xsl:call-template name="replace-substring">
+  <xsl:with-param name="string" select="/rfc/x:feedback/@template"/>
+  <xsl:with-param name="replace">"</xsl:with-param>
+  <xsl:with-param name="by">\"</xsl:with-param>
+</xsl:call-template>";
+
+    var id = node.getAttribute("id");
+    // try also parent
+    if (id == null || id == "") {
+      var id = node.parentNode.getAttribute("id");
+    }
+    // better id available?
+    var titlelinks = node.getElementsByTagName("a");
+    for (var i = 0; i &lt; titlelinks.length; i++) {
+      var tl = titlelinks.item(i);
+      if (tl.getAttribute("id")) {
+        id = tl.getAttribute("id");
+      }
+    }
+    
+    // ref
+    var ref = window.location.toString();
+    var hash = ref.indexOf("#");
+    if (hash != -1) {
+      ref = ref.substring(0, hash);
+    }
+    if (id != null &amp;&amp; id != "") {
+      ref += "#" + id;
+    }
+
+    // docname
+    var docname = "<xsl:value-of select="/rfc/@docName"/>";
+
+    // section
+    var section = node.textContent;
+    section = section.replace("\u00a0", " ").trim();
+
+    // build URI from template
+    var uri = template.replace("{docname}", encodeURIComponent(docname));
+    uri = uri.replace("{section}", encodeURIComponent(section));
+    uri = uri.replace("{ref}", encodeURIComponent(ref));
+
+    var button = document.createElement("a");
+    button.className = "<xsl:value-of select="concat($css-fbbutton,' ',$css-noprint)"/>";
+    button.setAttribute("href", uri);
+    button.appendChild(document.createTextNode("send feedback"));
+    node.appendChild(button);
+  }
+  else {
+    var buttons = node.getElementsByTagName("a");
+    for (var i = 0; i &lt; buttons.length; i++) {
+      var b = buttons.item(i);
+      if (b.className == "<xsl:value-of select="concat($css-fbbutton,' ',$css-noprint)"/>") {
+        node.removeChild(b);
+      }
+    }
+  }
+}</script>
+</xsl:if>
+<xsl:if test="$xml2rfc-ext-insert-metadata='yes' and $rfcno!=''">
+<script type="application/javascript">
+function getMeta(rfcno, container) {
+
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", "https://tools.ietf.org/draft/rfc" + rfcno + "/state.xml", true);
+  xhr.onload = function (e) {
+    if (xhr.readyState === 4) {
+      if (xhr.status === 200) {
+        var doc = xhr.responseXML;
+        var info = getChildByName(doc.documentElement, "info");
+  
+        var cont = document.getElementById(container);
+        // empty the container
+        while (cont.firstChild) {
+          cont.removeChild(myNode.firstChild);
+        }      
+  
+        var c = getChildByName(info, "stdstatus");
+        if (c !== null) {
+          var bld = newElementWithText("b", c.textContent);
+          cont.appendChild(bld);
+        }
+  
+        c = getChildByName(info, "updatedby");
+        if (c !== null) {
+          cont.appendChild(newElement("br"));
+          cont.appendChild(newText("Updated by: "));
+          appendRfcLinks(cont, c.textContent);
+        }
+  
+        c = getChildByName(info, "obsoletedby");
+        if (c !== null) {
+          cont.appendChild(newElement("br"));
+          cont.appendChild(newText("Obsoleted by: "));
+          appendRfcLinks(cont, c.textContent);
+        }
+        
+        c = getChildByName(info, "errata");
+        if (c !== null) {
+          var template = "<xsl:call-template name="replace-substring">
+              <xsl:with-param name="string" select="$xml2rfc-ext-rfc-errata-uri"/>
+              <xsl:with-param name="replace">"</xsl:with-param>
+              <xsl:with-param name="by">\"</xsl:with-param>
+            </xsl:call-template>";
+
+          cont.appendChild(newElement("br"));
+          var link = newElementWithText("a", "errata");
+          link.setAttribute("href", template.replace("{rfc}", rfcno));
+          var errata = newElementWithText("i", "This document has ");
+          errata.appendChild(link);
+          errata.appendChild(newText("."));
+          cont.appendChild(errata);
+        }
+
+        cont.style.display = "block";
+      } else {
+        console.error(xhr.statusText);
+      }
+    }
+  };
+  xhr.onerror = function (e) {
+    console.error(xhr.status + " " + xhr.statusText);
+  };
+  xhr.send(null);
+}
+
+// DOM helpers
+function newElement(name) {
+  return document.createElement(name);
+}
+function newElementWithText(name, txt) {
+  var e = document.createElement(name);
+  e.appendChild(newText(txt));
+  return e;
+}
+function newText(text) {
+  return document.createTextNode(text);
+}
+
+function getChildByName(parent, name) {
+  if (parent === null) {
+    return null;
+  }
+  else {
+    for (var c = parent.firstChild; c !== null; c = c.nextSibling) {
+      if (name == c.nodeName) {
+        return c;
+      }
+    }
+    return null;
+  }
+}
+
+function appendRfcLinks(parent, text) {
+  var template = "<xsl:call-template name="replace-substring">
+  <xsl:with-param name="string" select="$xml2rfc-ext-rfc-uri"/>
+  <xsl:with-param name="replace">"</xsl:with-param>
+  <xsl:with-param name="by">\"</xsl:with-param>
+</xsl:call-template>";
+  var updates = text.split(",");
+  for (var i = 0; i &lt; updates.length; i++) {
+    var rfc = updates[i].trim();
+    if (rfc.substring(0, 3) == "rfc") {
+      var no = rfc.substring(3);
+      var link = newElement("a");
+      link.setAttribute("href", template.replace("{rfc}", no));
+      link.appendChild(newText(no));
+      parent.appendChild(link);
+    } else {
+      parent.appendChild(newText(rfc));
+    }
+    if (i != updates.length - 1) {
+      parent.appendChild(newText(", "));
+    }
+  }
+}
+</script>
+</xsl:if>
+<script type="application/javascript">
+function anchorRewrite() {
+<xsl:text>  map = { </xsl:text>
+  <xsl:for-each select="//x:anchor-alias">
+    <xsl:text>"</xsl:text>
+    <xsl:call-template name="replace-substring">
+      <xsl:with-param name="string" select="@value"/>
+      <xsl:with-param name="replace">"</xsl:with-param>
+      <xsl:with-param name="by">\"</xsl:with-param>
+    </xsl:call-template>
+    <xsl:text>": "</xsl:text>
+    <xsl:call-template name="replace-substring">
+      <xsl:with-param name="string" select="ancestor::*[@anchor][1]/@anchor"/>
+      <xsl:with-param name="replace">"</xsl:with-param>
+      <xsl:with-param name="by">\"</xsl:with-param>
+    </xsl:call-template>
+    <xsl:text>"</xsl:text>
+    <xsl:if test="position()!=last()">, </xsl:if>
+  </xsl:for-each>
+<xsl:text>};</xsl:text>
+  if (window.location.hash.length >= 1) {
+    var fragid = window.location.hash.substr(1);
+    if (fragid) {
+      if (! document.getElementById(fragid)) {
+        var prefix = "<xsl:value-of select="$anchor-pref"/>";
+        var mapped = map[fragid];
+        if (mapped) {
+          window.location.hash = mapped;
+        } else if (fragid.indexOf("section-") == 0) {
+          window.location.hash = prefix + "section." + fragid.substring(8);
+        } else if (fragid.indexOf("appendix-") == 0) {
+          window.location.hash = prefix + "section." + fragid.substring(9);
+        } else if (fragid.indexOf("s-") == 0) {
+          var postfix = fragid.substring(2);
+          if (postfix.startsWith("abstract")) {
+            window.location.hash = prefix + postfix;
+          } else if (postfix.startsWith("note-")) {
+            window.location.hash = prefix + "note." + postfix.substring(5);
+          } else {
+            window.location.hash = prefix + "section." + postfix;
+          }
+        } else if (fragid.indexOf("p-") == 0) {
+          var r = fragid.substring(2);
+          var p = r.indexOf("-");
+          if (p >= 0) {
+            window.location.hash = prefix + "section." + r.substring(0, p) + ".p." + r.substring(p + 1);
+          }
+        }
+      }
+    }  
+  }
+}
+window.addEventListener('hashchange', anchorRewrite);
+window.addEventListener('DOMContentLoaded', anchorRewrite);
+</script><xsl:if test="$prettyprint-script!=''">
+<script src="{$prettyprint-script}"/></xsl:if>
+</xsl:template>
+
+<!-- insert CSS style info -->
+
+<xsl:template name="insertCss">
+<style type="text/css" title="rfc2629.xslt">
+<xsl:value-of select="$xml2rfc-ext-webfonts"/>
+a {
+  text-decoration: none;
+}
+a.smpl {
+  color: black;
+}
+a:hover {
+  text-decoration: underline;
+}
+a:active {
+  text-decoration: underline;
+}
+address {
+  margin-top: 1em;
+  margin-left: 2em;
+  font-style: normal;
+}<xsl:if test="//x:blockquote|//blockquote">
+blockquote {
+  border-style: solid;
+  border-color: gray;
+  border-width: 0 0 0 .25em;
+  font-style: italic;
+  padding-left: 0.5em;
+}</xsl:if>
+body {<xsl:if test="$xml2rfc-background!=''">
+  background: url(<xsl:value-of select="$xml2rfc-background" />) #ffffff left top;</xsl:if>
+  color: black;
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-body"/>;
+  font-size: 12pt;
+  margin: 10px 0px 10px 10px;
+}<xsl:if test="$parsedMaxwidth!=''">
+@media screen and (min-width: <xsl:value-of select="number($parsedMaxwidth + 40)"/>px) {
+  body {
+    margin: 10px auto;
+    max-width: <xsl:value-of select="$parsedMaxwidth"/>px;
+  }
+}</xsl:if>
+samp, span.tt, code, pre {
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-pre"/>;
+}<xsl:if test="//xhtml:p">
+br.p {
+  line-height: 150%;
+}</xsl:if>
+cite {
+  font-style: normal;
+}<xsl:if test="//x:note|//aside">
+aside {
+  margin-left: 2em;
+}</xsl:if>
+dl {
+  margin-left: 2em;
+}
+dl > dt {
+  float: left;
+  margin-right: 1em;
+}
+dl.nohang > dt {
+  float: none;
+}
+dl > dd {
+  margin-bottom: .5em;
+}
+dl.compact > dd {
+  margin-bottom: .0em;
+}
+dl > dd > dl {
+  margin-top: 0.5em;
+}
+ul.empty {<!-- spacing between two entries in definition lists -->
+  list-style-type: none;
+}
+ul.empty li {
+  margin-top: .5em;
+}
+dl p {
+  margin-left: 0em;
+}
+dl.<xsl:value-of select="$css-reference"/> > dt {
+  font-weight: bold;
+}
+dl.<xsl:value-of select="$css-reference"/> > dd {
+  margin-left: <xsl:choose><xsl:when test="$xml2rfc-symrefs='no'">3.5</xsl:when><xsl:otherwise>6</xsl:otherwise></xsl:choose>em;
+}
+h1 {
+  color: green;
+  font-size: 150%;
+  line-height: 18pt;
+  font-weight: bold;
+  text-align: center;
+  margin-top: 36pt;
+  margin-bottom: 0pt;
+}
+h2 {
+  font-size: 130%;
+  line-height: 21pt;
+  page-break-after: avoid;
+}
+h2.np {
+  page-break-before: always;
+}
+h3 {
+  font-size: 120%;
+  line-height: 15pt;
+  page-break-after: avoid;
+}
+h4 {
+  font-size: 110%;
+  page-break-after: avoid;
+}
+h5, h6 {
+  page-break-after: avoid;
+}
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+  color: black;
+}
+img {
+  margin-left: 3em;
+}
+li {
+  margin-left: 2em;
+}
+ol {
+  margin-left: 2em;
+}
+ol.la {
+  list-style-type: lower-alpha;
+}
+ol.ua {
+  list-style-type: upper-alpha;
+}
+ol p {
+  margin-left: 0em;
+}<xsl:if test="//xhtml:q">
+q {
+  font-style: italic;
+}</xsl:if>
+p {
+  margin-left: 2em;
+}
+pre {
+  font-size: 11pt;
+  margin-left: 3em;
+  background-color: lightyellow;
+  padding: .25em;
+  page-break-inside: avoid;
+}<xsl:if test="//artwork[@x:is-code-component='yes']"><!-- support "<CODE BEGINS>" and "<CODE ENDS>" markers-->
+pre.ccmarker {
+  background-color: white;
+  color: gray;
+}
+pre.ccmarker > span {
+  font-size: small;
+}
+pre.cct {
+  margin-bottom: -1em;
+}
+pre.ccb {
+  margin-top: -1em;
+}</xsl:if>
+pre.text2 {
+  border-style: dotted;
+  border-width: 1px;
+  background-color: #f0f0f0;
+}
+pre.inline {
+  background-color: white;
+  padding: 0em;
+  page-break-inside: auto;<xsl:if test="$prettyprint-script!=''">
+  border: none !important;</xsl:if>
+}
+pre.text {
+  border-style: dotted;
+  border-width: 1px;
+  background-color: #f8f8f8;
+}
+pre.drawing {
+  border-style: solid;
+  border-width: 1px;
+  background-color: #f8f8f8;
+  padding: 2em;
+}<xsl:if test="//x:q">
+q {
+  font-style: italic;
+}</xsl:if>
+<xsl:if test="//x:sup|sup">
+sup {
+  font-size: 60%;
+}</xsl:if><xsl:if test="sub">
+sub {
+  font-size: 60%;
+}</xsl:if>
+table {
+  margin-left: 2em;
+}<xsl:if test="//texttable|//table">
+div.<xsl:value-of select="$css-tt"/> {
+  margin-left: 2em;
+} 
+table.<xsl:value-of select="$css-tt"/> {
+  border-collapse: collapse;
+  border-color: gray;
+  border-spacing: 0; 
+  vertical-align: top;
+ }
+table.<xsl:value-of select="$css-tt"/> th {
+  border-color: gray;
+  padding: 3px;
+}
+table.<xsl:value-of select="$css-tt"/> td {
+  border-color: gray;
+  padding: 3px;
+}
+table.all {
+  border-style: solid;
+  border-width: 2px;
+}
+table.full {
+  border-style: solid;
+  border-width: 2px;
+}
+table.<xsl:value-of select="$css-tt"/> td {
+  vertical-align: top;
+}
+table.all td {
+  border-style: solid;
+  border-width: 1px;
+}
+table.full td {
+  border-style: none solid;
+  border-width: 1px;
+}
+table.<xsl:value-of select="$css-tt"/> th {
+  vertical-align: top;
+}
+table.all th {
+  border-style: solid;
+  border-width: 1px;
+}
+table.full th {
+  border-style: solid;
+  border-width: 1px 1px 2px 1px;
+}
+table.headers th {
+  border-style: none none solid none;
+  border-width: 2px;
+}
+table.<xsl:value-of select="$css-tleft"/> {
+  margin-right: auto;
+}
+table.<xsl:value-of select="$css-tright"/> {
+  margin-left: auto;
+}
+table.<xsl:value-of select="$css-tcenter"/> {
+  margin-left: auto;
+  margin-right: auto;
+}
+caption {
+  caption-side: bottom;
+  font-weight: bold;
+  font-size: 10pt;
+  margin-top: .5em;
+}
+<xsl:if test="//table">
+table:not([class]) th {
+  background-color: #e9e9e9;
+}
+table:not([class]) tr:nth-child(2n) > td {
+  background-color: #f5f5f5;
+}
+tr p {
+  margin-left: 0em;
+}
+tr pre {
+  margin-left: 1em;
+}
+tr ol {
+  margin-left: 1em;
+}
+tr ul {
+  margin-left: 1em;
+}
+tr dl {
+  margin-left: 1em;
+}
+</xsl:if>
+</xsl:if>
+table.<xsl:value-of select="$css-header"/> {
+  border-spacing: 1px;
+  width: 95%;
+  font-size: 11pt;
+  color: white;
+}
+td.top {
+  vertical-align: top;
+}
+td.topnowrap {
+  vertical-align: top;
+  white-space: nowrap;
+}
+table.<xsl:value-of select="$css-header"/> td {
+  background-color: gray;
+  width: 50%;
+}<xsl:if test="/rfc/@obsoletes | /rfc/@updates">
+table.<xsl:value-of select="$css-header"/> a {
+  color: white;
+}</xsl:if>
+ul.toc, ul.toc ul {
+  list-style: none;
+  margin-left: 1.5em;
+  padding-left: 0em;
+}
+ul.toc li {
+  line-height: 150%;
+  font-weight: bold;
+  margin-left: 0em;
+}
+ul.toc li li {
+  line-height: normal;
+  font-weight: normal;
+  font-size: 11pt;
+  margin-left: 0em;
+}
+li.excluded {
+  font-size: 0pt;
+}
+ul p {
+  margin-left: 0em;
+}
+.filename, h1, h2, h3, h4 {
+  font-family: <xsl:value-of select="$xml2rfc-ext-ff-title"/>;
+}
+<xsl:if test="$has-index">ul.ind, ul.ind ul {
+  list-style: none;
+  margin-left: 1.5em;
+  padding-left: 0em;
+  page-break-before: avoid;
+}
+ul.ind li {
+  font-weight: bold;
+  line-height: 200%;
+  margin-left: 0em;
+}
+ul.ind li li {
+  font-weight: normal;
+  line-height: 150%;
+  margin-left: 0em;
+}<xsl:if test="//svg:svg">
+@namespace svg url(http://www.w3.org/2000/svg);
+svg|svg {
+  margin-left: 3em;
+}
+svg {
+  margin-left: 3em;
+}</xsl:if>
+.avoidbreakinside {
+  page-break-inside: avoid;
+}
+.avoidbreakafter {
+  page-break-after: avoid;
+}
+</xsl:if><xsl:if test="//*[@removeInRFC='true']">.rfcEditorRemove div:first-of-type {
+  font-style: italic;
+}</xsl:if><xsl:if test="//x:bcp14|//bcp14">.bcp14 {
+  font-style: normal;
+  text-transform: lowercase;
+  font-variant: small-caps;
+}</xsl:if><xsl:if test="//x:blockquote|//blockquote">
+blockquote > * .bcp14 {
+  font-style: italic;
+}</xsl:if>
+.comment {
+  background-color: yellow;
+}<xsl:if test="$xml2rfc-editing='yes'">
+.editingmark {
+  background-color: khaki;
+}</xsl:if>
+.<xsl:value-of select="$css-center"/> {
+  text-align: center;
+}
+.<xsl:value-of select="$css-error"/> {
+  color: red;
+  font-style: italic;
+  font-weight: bold;
+}
+.figure {
+  font-weight: bold;
+  text-align: center;
+  font-size: 10pt;
+}
+.filename {
+  color: #333333;
+  font-size: 112%;
+  font-weight: bold;
+  line-height: 21pt;
+  text-align: center;
+  margin-top: 0pt;
+}
+.fn {
+  font-weight: bold;
+}
+.<xsl:value-of select="$css-left"/> {
+  text-align: left;
+}
+.<xsl:value-of select="$css-right"/> {
+  text-align: right;
+}
+.warning {
+  font-size: 130%;
+  background-color: yellow;
+}<xsl:if test="$xml2rfc-ext-paragraph-links='yes'">
+.self {
+    color: #999999;
+    margin-left: .3em;
+    text-decoration: none;
+    visibility: hidden;
+    -webkit-user-select: none;<!-- not std CSS yet--> 
+    -moz-user-select: none;
+    -ms-user-select: none;
+}
+.self:hover {
+    text-decoration: none;
+}
+li:hover > a.self, p:hover > a.self {
+    visibility: visible;
+}</xsl:if><xsl:if test="$has-edits">del {
+  color: red;
+  text-decoration: line-through;
+}
+.del {
+  color: red;
+  text-decoration: line-through;
+}
+ins {
+  color: green;
+  text-decoration: underline;
+}
+.ins {
+  color: green;
+  text-decoration: underline;
+}
+div.issuepointer {
+  float: left;
+}</xsl:if><xsl:if test="//ed:issue">
+table.openissue {
+  background-color: khaki;
+  border-width: thin;
+  border-style: solid;
+  border-color: black;
+}
+table.closedissue {
+  background-color: white;
+  border-width: thin;
+  border-style: solid;
+  border-color: gray;
+  color: gray;
+}
+thead th {
+  text-align: left;
+}
+.bg-issue {
+  border: solid;
+  border-width: 1px;
+  font-size: 8pt;
+}
+.closed-issue {
+  border: solid;
+  border-width: thin;
+  background-color: lime;
+  font-size: smaller;
+  font-weight: bold;
+}
+.open-issue {
+  border: solid;
+  border-width: thin;
+  background-color: red;
+  font-size: smaller;
+  font-weight: bold;
+}
+.editor-issue {
+  border: solid;
+  border-width: thin;
+  background-color: yellow;
+  font-size: smaller;
+  font-weight: bold;
+}</xsl:if><xsl:if test="$xml2rfc-ext-refresh-from!=''">.refreshxmlerror {
+  position: fixed;
+  top: 1%;
+  right: 1%;
+  padding: 5px 5px;
+  color: yellow;
+  background: black;
+}
+.refreshbrowsererror {
+  position: fixed;
+  top: 1%;
+  left: 1%;
+  padding: 5px 5px;
+  color: red;
+  background: black;
+}</xsl:if><xsl:if test="/rfc/x:feedback">.<xsl:value-of select="$css-feedback"/> {
+  position: fixed;
+  bottom: 1%;
+  right: 1%;
+  padding: 3px 5px;
+  color: white;
+  border-radius: 5px;
+  background: #006400;
+  border: 1px solid silver;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}
+.<xsl:value-of select="$css-fbbutton"/> {
+  margin-left: 1em;
+  color: #303030;
+  font-size: small;
+  font-weight: normal;
+  background: #d0d000;
+  padding: 1px 4px;
+  border: 1px solid silver;
+  border-radius: 5px;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}</xsl:if><xsl:if test="$xml2rfc-ext-justification='always'">
+dd, li, p {
+  text-align: justify;
+}</xsl:if><xsl:if test="$xml2rfc-ext-insert-metadata='yes' and $rfcno!=''">
+.<xsl:value-of select="$css-docstatus"/> {
+  border: 1px solid black;
+  display: none;
+  float: right;
+  margin: 2em;
+  padding: 1em;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}</xsl:if><xsl:if test="$errata-parsed">
+.<xsl:value-of select="$css-erratum"/> {
+  border: 1px solid orangered;
+  border-left: 0.75em solid orangered;
+  float: right;
+  padding: 0.5em;
+  -webkit-user-select: none;<!-- not std CSS yet--> 
+  -moz-user-select: none;
+  -ms-user-select: none;
+}<xsl:if test="$parsedMaxwidth!=''">
+@media screen and (min-width: <xsl:value-of select="number($parsedMaxwidth + 350)"/>px) {
+  .<xsl:value-of select="$css-erratum"/> {
+    margin-right: -150px;
+  }
+}</xsl:if></xsl:if><xsl:if test="$published-as-rfc">
+.<xsl:value-of select="$css-publishedasrfc"/> {
+  background-color: yellow;
+  color: green;
+  font-size: 14pt;
+  text-align: center;
+}</xsl:if>
+
+@media screen {
+  pre.text, pre.text2 {
+    width: 69em;
+  }
+}
+
+@media print {
+  .<xsl:value-of select="$css-noprint"/> {
+    display: none;
+  }
+
+  a {
+    color: black;
+    text-decoration: none;
+  }
+
+  table.<xsl:value-of select="$css-header"/> {
+    width: 90%;
+  }
+
+  td.<xsl:value-of select="$css-header"/> {
+    width: 50%;
+    color: black;
+    background-color: white;
+    vertical-align: top;
+    font-size: 110%;
+  }
+
+  ul.toc a:last-child::after {
+    content: leader('.') target-counter(attr(href), page);
+  }
+
+  ul.ind li li a {<!-- links in the leaf nodes of the index should go to page numbers -->
+    content: target-counter(attr(href), page);
+  }
+
+  pre {
+    font-size: 10pt;
+  }
+
+  .print2col {
+    column-count: 2;
+    -moz-column-count: 2;<!-- for Firefox -->
+    column-fill: auto;<!-- for PrinceXML -->
+  }
+<xsl:if test="$xml2rfc-ext-justification='print'">
+  dd, li, p {
+    text-align: justify;
+  }
+</xsl:if>}
+@page<xsl:if test="$xml2rfc-ext-duplex='yes'">:right</xsl:if> {
+  @top-left {
+       content: "<xsl:call-template name="get-header-left"/>";
+  }
+  @top-right {
+       content: "<xsl:call-template name="get-header-right"/>";
+  }
+  @top-center {
+       content: "<xsl:call-template name="get-header-center"/>";
+  }
+  @bottom-left {
+       content: "<xsl:call-template name="get-author-summary"/>";
+  }
+  @bottom-center {
+       content: "<xsl:call-template name="get-bottom-center"/>";
+  }
+  @bottom-right {
+       content: "[Page " counter(page) "]";
+  }
+}<xsl:if test="$xml2rfc-ext-duplex='yes'">
+@page:left {
+  @top-left {
+       content: "<xsl:call-template name="get-header-right"/>";
+  }
+  @top-right {
+       content: "<xsl:call-template name="get-header-left"/>";
+  }
+  @top-center {
+       content: "<xsl:call-template name="get-header-center"/>";
+  }
+  @bottom-left {
+       content: "[Page " counter(page) "]";
+  }
+  @bottom-center {
+       content: "<xsl:call-template name="get-bottom-center"/>";
+  }
+  @bottom-right {
+       content: "<xsl:call-template name="get-author-summary"/>";
+  }
+}
+</xsl:if>
+@page:first {
+    @top-left {
+      content: normal;
+    }
+    @top-right {
+      content: normal;
+    }
+    @top-center {
+      content: normal;
+    }
+}
+</style>
+</xsl:template>
+
+
+<!-- generate the index section -->
+
+<xsl:template name="insertSingleIref">
+  <xsl:choose>
+    <xsl:when test="@ed:xref">
+      <!-- special index generator mode -->
+      <xsl:text>[</xsl:text>
+      <a href="#{@ed:xref}"><xsl:value-of select="@ed:xref"/></a>
+      <xsl:text>, </xsl:text>
+      <a>
+        <xsl:variable name="htmluri" select="//reference[@anchor=current()/@ed:xref]/format[@type='HTML']/@target"/>
+        <xsl:if test="$htmluri">
+          <xsl:attribute name="href"><xsl:value-of select="concat($htmluri,'#',@ed:frag)"/></xsl:attribute>
+        </xsl:if>
+        <xsl:choose>
+          <xsl:when test="@primary='true'"><b><xsl:value-of select="@ed:label" /></b></xsl:when>
+          <xsl:otherwise><xsl:value-of select="@ed:label" /></xsl:otherwise>
+        </xsl:choose>
+      </a>
+      <xsl:text>]</xsl:text>
+      <xsl:if test="position()!=last()">, </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="_n">
+        <xsl:call-template name="get-section-number" />
+      </xsl:variable>
+      <xsl:variable name="n">
+        <xsl:choose>
+          <xsl:when test="$_n!=''">
+            <xsl:value-of select="$_n"/>
+          </xsl:when>
+          <xsl:otherwise>&#167;</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <xsl:variable name="backlink">
+        <xsl:choose>
+          <xsl:when test="self::xref">
+            <xsl:variable name="target" select="@target"/>
+            <xsl:comment>workaround for Saxon 9.1 bug; force evaluation of: <xsl:value-of select="$target"/></xsl:comment>
+            <xsl:variable name="no"><xsl:number level="any" count="xref[@target=$target]"/></xsl:variable>
+            <xsl:text>#</xsl:text>
+            <xsl:value-of select="$anchor-pref"/>
+            <xsl:text>xref.</xsl:text>
+            <xsl:value-of select="@target"/>
+            <xsl:text>.</xsl:text>
+            <xsl:value-of select="$no"/>
+          </xsl:when>
+          <xsl:when test="self::iref">
+            <xsl:text>#</xsl:text>
+            <xsl:call-template name="compute-iref-anchor"/>
+          </xsl:when>
+          <xsl:when test="self::x:ref">
+            <xsl:text>#</xsl:text>
+            <xsl:call-template name="compute-extref-anchor"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:message>Unsupported element type for insertSingleIref</xsl:message>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <a href="{$backlink}">
+        <xsl:call-template name="insertInsDelClass"/>
+        <xsl:choose>
+          <xsl:when test="@primary='true'"><b><xsl:value-of select="$n"/></b></xsl:when>
+          <xsl:otherwise><xsl:value-of select="$n"/></xsl:otherwise>
+        </xsl:choose>
+      </a>
+      <xsl:if test="position()!=last()">, </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="insertSingleXref">
+  <xsl:variable name="_n">
+    <xsl:call-template name="get-section-number" />
+  </xsl:variable>
+  <xsl:variable name="n">
+    <xsl:choose>
+      <xsl:when test="$_n!=''">
+        <xsl:value-of select="$_n"/>
+      </xsl:when>
+      <xsl:otherwise>&#167;</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="self::reference">
+      <a href="#{@anchor}">
+        <xsl:call-template name="insertInsDelClass"/>
+        <b><xsl:value-of select="$n"/></b>
+      </a>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="target" select="@target"/>
+      <xsl:variable name="backlink">#<xsl:value-of select="$anchor-pref"/>xref.<xsl:value-of select="$target"/>.<xsl:number level="any" count="xref[@target=$target]"/></xsl:variable>
+      <a href="{$backlink}">
+        <xsl:call-template name="insertInsDelClass"/>
+        <xsl:value-of select="$n"/>
+      </a>
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:if test="position()!=last()">, </xsl:if>
+</xsl:template>
+
+<xsl:template name="insertIndex">
+
+  <xsl:call-template name="insert-conditional-hrule"/>
+
+  <section id="{$anchor-pref}index">
+    <xsl:call-template name="insert-conditional-pagebreak"/>
+    <h2>
+      <a href="#{$anchor-pref}index">Index</a>
+    </h2>
+
+    <!-- generate navigation links to index subsections -->
+    <p class="{$css-noprint}">
+      <xsl:variable name="irefs" select="//iref[generate-id(.) = generate-id(key('index-first-letter',translate(substring(@item,1,1),$lcase,$ucase))[1])]"/>
+      <xsl:variable name="xrefs" select="//reference[not(starts-with(@anchor,'deleted-'))][generate-id(.) = generate-id(key('index-first-letter',translate(substring(concat(/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),1,1),$lcase,$ucase))[1])]"/>
+  
+      <xsl:for-each select="$irefs | $xrefs">
+      
+        <xsl:sort select="translate(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),$lcase,$ucase)" />
+  
+        <xsl:variable name="letter" select="translate(substring(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),1,1),$lcase,$ucase)"/>
+  
+        <!-- character? -->
+        <xsl:if test="translate($letter,concat($lcase,$ucase,'0123456789'),'')=''">
+  
+          <xsl:variable name="showit" select="$xml2rfc-ext-include-references-in-index='yes' or $irefs[starts-with(translate(@item,$lcase,$ucase),$letter)]"/>
+  
+          <xsl:if test="$showit">
+            <a href="#{$anchor-pref}index.{$letter}">
+              <xsl:value-of select="$letter" />
+            </a>
+            <xsl:text> </xsl:text>
+          </xsl:if>
+        </xsl:if>
+      </xsl:for-each>
+    </p>
+
+    <!-- for each index subsection -->
+    <div class="print2col">
+      <ul class="ind">
+        <xsl:variable name="irefs2" select="//iref[generate-id(.) = generate-id(key('index-first-letter',translate(substring(@item,1,1),$lcase,$ucase))[1])]"/>
+        <xsl:variable name="xrefs2" select="//reference[not(starts-with(@anchor,'deleted-'))][generate-id(.) = generate-id(key('index-first-letter',translate(substring(concat(/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),1,1),$lcase,$ucase))[1])]"/>
+    
+        <xsl:for-each select="$irefs2 | $xrefs2">
+          <xsl:sort select="translate(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),$lcase,$ucase)" />
+          <xsl:variable name="letter" select="translate(substring(concat(@item,/rfc/back/displayreference[@target=current()/@anchor]/@to,@anchor),1,1),$lcase,$ucase)"/>
+    
+          <xsl:variable name="showit" select="$xml2rfc-ext-include-references-in-index='yes' or $irefs2[starts-with(translate(@item,$lcase,$ucase),$letter)]"/>
+    
+          <xsl:if test="$showit">
+            <li>
+    
+              <!-- make letters and digits stand out -->
+              <xsl:choose>
+                <xsl:when test="translate($letter,concat($lcase,$ucase,'0123456789'),'')=''">
+                  <a id="{$anchor-pref}index.{$letter}" href="#{$anchor-pref}index.{$letter}">
+                    <b><xsl:value-of select="$letter" /></b>
+                  </a>
+                </xsl:when>
+                <xsl:otherwise>
+                  <b><xsl:value-of select="$letter" /></b>
+                </xsl:otherwise>
+              </xsl:choose>
+    
+              <ul>
+                <xsl:for-each select="key('index-first-letter',translate(substring(concat(@item,@anchor),1,1),$lcase,$ucase))">
+    
+                  <xsl:sort select="translate(concat(@item,@anchor),$lcase,$ucase)" />
+    
+                  <xsl:choose>
+                    <xsl:when test="self::reference">
+                      <xsl:if test="$xml2rfc-ext-include-references-in-index='yes' and not(starts-with(@anchor,'deleted-'))">
+                        <li>
+                          <xsl:variable name="val">
+                            <xsl:call-template name="reference-name"/>
+                          </xsl:variable>
+                          <em>
+                            <xsl:value-of select="substring($val,2,string-length($val)-2)"/>
+                          </em>
+                          <xsl:text>&#160;&#160;</xsl:text>
+    
+                          <xsl:variable name="rs" select="key('xref-item',current()/@anchor) | . | key('anchor-item',concat('deleted-',current()/@anchor))"/>
+    
+                          <xsl:for-each select="$rs">
+                            <xsl:call-template name="insertSingleXref" />
+                          </xsl:for-each>
+    
+                          <xsl:variable name="rs2" select="$rs[@x:sec|@section]"/>
+    
+                          <xsl:if test="$rs2">
+                            <ul>
+                              <xsl:for-each select="$rs2">
+                                <xsl:sort select="substring-before(concat(@x:sec,@section,'.'),'.')" data-type="number"/>
+                                <xsl:sort select="substring(concat(@x:sec,@section),2+string-length(substring-before(concat(@x:sec,@section),'.')))" data-type="number"/>
+    
+                                <xsl:if test="generate-id(.) = generate-id(key('index-xref-by-sec',concat(@target,'..',@x:sec,@section))[1])">
+                                  <li>
+                                    <em>
+                                      <xsl:choose>
+                                        <xsl:when test="translate(substring(concat(@x:sec,@section),1,1),$ucase,'')=''">
+                                          <xsl:text>Appendix </xsl:text>
+                                        </xsl:when>
+                                        <xsl:otherwise>
+                                          <xsl:text>Section </xsl:text>
+                                        </xsl:otherwise>
+                                      </xsl:choose>
+                                      <xsl:value-of select="@x:sec|@section"/>
+                                    </em>
+                                    <xsl:text>&#160;&#160;</xsl:text>
+                                    <xsl:for-each select="key('index-xref-by-sec',concat(@target,'..',@x:sec,@section))">
+                                      <xsl:call-template name="insertSingleXref" />
+                                    </xsl:for-each>
+                                  </li>
+                                </xsl:if>
+                              </xsl:for-each>
+                            </ul>
+                          </xsl:if>
+    
+                          <xsl:if test="current()/x:source/@href">
+                            <xsl:variable name="rs3" select="$rs[not(@x:sec) and @x:rel]"/>
+                            <xsl:variable name="doc" select="document(current()/x:source/@href)"/>
+                            <xsl:if test="$rs3">
+                              <ul>
+                                <xsl:for-each select="$rs3">
+                                  <xsl:sort select="count($doc//*[@anchor and following::*/@anchor=substring-after(current()/@x:rel,'#')])" order="ascending" data-type="number"/>
+                                  <xsl:if test="generate-id(.) = generate-id(key('index-xref-by-anchor',concat(@target,'..',@x:rel))[1])">
+                                    <li>
+                                      <em>
+                                        <xsl:variable name="sec">
+                                          <xsl:for-each select="$doc//*[@anchor=substring-after(current()/@x:rel,'#')]">
+                                            <xsl:call-template name="get-section-number"/>
+                                          </xsl:for-each>
+                                        </xsl:variable>
+                                        <xsl:choose>
+                                          <xsl:when test="translate(substring($sec,1,1),$ucase,'')=''">
+                                            <xsl:text>Appendix </xsl:text>
+                                          </xsl:when>
+                                          <xsl:otherwise>
+                                            <xsl:text>Section </xsl:text>
+                                          </xsl:otherwise>
+                                        </xsl:choose>
+                                        <xsl:value-of select="$sec"/>
+                                      </em>
+                                      <xsl:text>&#160;&#160;</xsl:text>
+                                      <xsl:for-each select="key('index-xref-by-anchor',concat(@target,'..',@x:rel))">
+                                        <xsl:call-template name="insertSingleXref" />
+                                      </xsl:for-each>
+                                    </li>
+                                  </xsl:if>
+                                </xsl:for-each>
+                              </ul>
+                            </xsl:if>
+                          </xsl:if>
+                        </li>
+                      </xsl:if>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      <!-- regular iref -->
+                      <xsl:if test="generate-id(.) = generate-id(key('index-item',concat(@item,@anchor))[1])">
+                        <xsl:variable name="item" select="@item"/>
+                        <xsl:variable name="in-artwork" select="key('index-item',$item)[@primary='true' and ancestor::artwork]"/>
+    
+                        <li>
+                          <xsl:choose>
+                            <xsl:when test="$in-artwork">
+                              <span class="tt"><xsl:value-of select="@item" /></span>
+                            </xsl:when>
+                            <xsl:otherwise>
+                              <xsl:value-of select="@item" />
+                            </xsl:otherwise>
+                          </xsl:choose>
+                          <xsl:text>&#160;&#160;</xsl:text>
+    
+                          <xsl:variable name="irefs3" select="key('index-item',@item)[not(@subitem) or @subitem='']"/>
+                          <xsl:variable name="xrefs3" select="key('xref-item',$irefs3[@x:for-anchor='']/../@anchor) | key('xref-item',$irefs3/@x:for-anchor)"/>
+                          <xsl:variable name="extrefs3" select="key('extref-item',$irefs3[@x:for-anchor='']/../@anchor) | key('extref-item',$irefs3/@x:for-anchor)"/>
+    
+                          <xsl:for-each select="$irefs3|$xrefs3|$extrefs3">
+                            <!-- <xsl:sort select="translate(@item,$lcase,$ucase)" />  -->
+                            <xsl:call-template name="insertSingleIref" />
+                          </xsl:for-each>
+    
+                          <xsl:variable name="s2" select="key('index-item',@item)[@subitem and @subitem!='']"/>
+                          <xsl:if test="$s2">
+                            <ul>
+                              <xsl:for-each select="$s2">
+                                <xsl:sort select="translate(@subitem,$lcase,$ucase)" />
+    
+                                <xsl:if test="generate-id(.) = generate-id(key('index-item-subitem',concat(@item,'..',@subitem))[1])">
+    
+                                  <xsl:variable name="in-artwork2" select="key('index-item-subitem',concat(@item,'..',@subitem))[@primary='true' and ancestor::artwork]" />
+    
+                                  <li>
+    
+                                    <xsl:choose>
+                                      <xsl:when test="$in-artwork2">
+                                        <span class="tt"><xsl:value-of select="@subitem" /></span>
+                                      </xsl:when>
+                                      <xsl:otherwise>
+                                        <xsl:value-of select="@subitem" />
+                                      </xsl:otherwise>
+                                    </xsl:choose>
+                                    <xsl:text>&#160;&#160;</xsl:text>
+    
+                                    <xsl:variable name="irefs4" select="key('index-item-subitem',concat(@item,'..',@subitem))"/>
+                                    <xsl:variable name="xrefs4" select="key('xref-item',$irefs4[@x:for-anchor='']/../@anchor) | key('xref-item',$irefs4/@x:for-anchor)"/>
+                                    <xsl:variable name="extrefs4" select="key('extref-item',$irefs4[@x:for-anchor='']/../@anchor) | key('extref-item',$irefs4/@x:for-anchor)"/>
+    
+                                    <xsl:for-each select="$irefs4|$xrefs4|$extrefs4">
+                                      <!--<xsl:sort select="translate(@item,$lcase,$ucase)" />-->
+                                      <xsl:call-template name="insertSingleIref" />
+                                    </xsl:for-each>
+    
+                                  </li>
+                                </xsl:if>
+                              </xsl:for-each>
+                            </ul>
+                          </xsl:if>
+                        </li>
+                      </xsl:if>
+                    </xsl:otherwise>
+                  </xsl:choose>
+    
+    
+                </xsl:for-each>
+              </ul>
+            </li>
+          </xsl:if>
+    
+        </xsl:for-each>
+      </ul>
+    </div>
+  </section>
+</xsl:template>
+
+<xsl:template name="insertPreamble" myns:namespaceless-elements="xml2rfc">
+
+  <xsl:param name="notes"/>
+
+<boilerplate>
+  <!-- TLP4, Section 6.c.iii -->
+  <xsl:variable name="pre5378EscapeClause">
+    This document may contain material from IETF Documents or IETF Contributions published or
+    made publicly available before November 10, 2008. The person(s) controlling the copyright in
+    some of this material may not have granted the IETF Trust the right to allow modifications of such
+    material outside the IETF Standards Process. Without obtaining an adequate license from the
+    person(s) controlling the copyright in such materials, this document may not be modified outside
+    the IETF Standards Process, and derivative works of it may not be created outside the IETF
+    Standards Process, except to format it for publication as an RFC or to translate it into languages
+    other than English.
+  </xsl:variable>
+
+  <!-- TLP1, Section 6.c.i -->
+  <xsl:variable name="noModificationTrust200811Clause">
+    This document may not be modified, and derivative works of it may not be
+    created, except to format it for publication as an RFC and to translate it
+    into languages other than English.
+  </xsl:variable>
+
+  <!-- TLP2..4, Section 6.c.i -->
+  <xsl:variable name="noModificationTrust200902Clause">
+    This document may not be modified, and derivative works of it may not be
+    created, except to format it for publication as an RFC or to translate it
+    into languages other than English.<!-- "and" changes to "or" -->
+  </xsl:variable>
+
+  <!-- TLP1..4, Section 6.c.ii -->
+  <xsl:variable name="noDerivativesTrust200___Clause">
+    This document may not be modified, and derivative works of it may not be
+    created, and it may not be published except as an Internet-Draft.
+  </xsl:variable>
+
+  <section anchor="{$anchor-pref}status">
+  <name>
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-rfcedstyle='yes'">Status of This Memo</xsl:when>
+      <xsl:otherwise>Status of this Memo</xsl:otherwise>
+    </xsl:choose>
+  </name>
+
+  <xsl:choose>
+    <xsl:when test="@ipr and not(@number)">
+      <t>
+        <xsl:choose>
+
+          <!-- RFC2026 -->
+          <xsl:when test="@ipr = 'full2026'">
+            This document is an Internet-Draft and is
+            in full conformance with all provisions of Section 10 of RFC2026.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativeWorks2026'">
+            This document is an Internet-Draft and is
+            in full conformance with all provisions of Section 10 of RFC2026
+            except that the right to produce derivative works is not granted.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativeWorksNow'">
+            This document is an Internet-Draft and is
+            in full conformance with all provisions of Section 10 of RFC2026
+            except that the right to produce derivative works is not granted.
+            (If this document becomes part of an IETF working group activity,
+            then it will be brought into full compliance with Section 10 of RFC2026.)
+          </xsl:when>
+          <xsl:when test="@ipr = 'none'">
+            This document is an Internet-Draft and is
+            NOT offered in accordance with Section 10 of RFC2026,
+            and the author does not provide the IETF with any rights other
+            than to publish as an Internet-Draft.
+          </xsl:when>
+
+          <!-- RFC3667 -->
+          <xsl:when test="@ipr = 'full3667'">
+            This document is an Internet-Draft and is subject to all provisions
+            of section 3 of RFC 3667.  By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she become aware will be disclosed, in accordance with
+            RFC 3668.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noModification3667'">
+            This document is an Internet-Draft and is subject to all provisions
+            of section 3 of RFC 3667.  By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she become aware will be disclosed, in accordance with
+            RFC 3668.  This document may not be modified, and derivative works of
+            it may not be created, except to publish it as an RFC and to
+            translate it into languages other than English<xsl:if test="@iprExtract">,
+            other than to extract <xref target="{@iprExtract}"/> as-is
+            for separate use</xsl:if>.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivatives3667'">
+            This document is an Internet-Draft and is subject to all provisions
+            of section 3 of RFC 3667 except for the right to produce derivative
+            works.  By submitting this Internet-Draft, each author represents
+            that any applicable patent or other IPR claims of which he or she
+            is aware have been or will be disclosed, and any of which he or she
+            become aware will be disclosed, in accordance with RFC 3668.  This
+            document may not be modified, and derivative works of it may
+            not be created<xsl:if test="@iprExtract">, other than to extract
+            <xref target="{@iprExtract}"/> as-is for separate use</xsl:if>.
+          </xsl:when>
+
+          <!-- RFC3978 -->
+          <xsl:when test="@ipr = 'full3978'">
+            By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she becomes aware will be disclosed, in accordance with
+            Section 6 of BCP 79.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noModification3978'">
+            By submitting this Internet-Draft, each
+            author represents that any applicable patent or other IPR claims of
+            which he or she is aware have been or will be disclosed, and any of
+            which he or she becomes aware will be disclosed, in accordance with
+            Section 6 of BCP 79.  This document may not be modified, and derivative works of
+            it may not be created, except to publish it as an RFC and to
+            translate it into languages other than English<xsl:if test="@iprExtract">,
+            other than to extract <xref target="{@iprExtract}"/> as-is
+            for separate use</xsl:if>.
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivatives3978'">
+            By submitting this Internet-Draft, each author represents
+            that any applicable patent or other IPR claims of which he or she
+            is aware have been or will be disclosed, and any of which he or she
+            becomes aware will be disclosed, in accordance with Section 6 of BCP 79.  This
+            document may not be modified, and derivative works of it may
+            not be created<xsl:if test="@iprExtract">, other than to extract
+            <xref target="{@iprExtract}"/> as-is for separate use</xsl:if>.
+          </xsl:when>
+
+          <!-- as of Jan 2010, TLP 4.0 -->
+          <xsl:when test="$ipr-2010-01 and (@ipr = 'trust200902'
+                          or @ipr = 'noModificationTrust200902'
+                          or @ipr = 'noDerivativesTrust200902'
+                          or @ipr = 'pre5378Trust200902')">
+            This Internet-Draft is submitted in full conformance with
+            the provisions of BCP 78 and BCP 79.
+          </xsl:when>
+
+          <!-- as of Nov 2008, Feb 2009 and Sep 2009 -->
+          <xsl:when test="@ipr = 'trust200811'
+                          or @ipr = 'noModificationTrust200811'
+                          or @ipr = 'noDerivativesTrust200811'
+                          or @ipr = 'trust200902'
+                          or @ipr = 'noModificationTrust200902'
+                          or @ipr = 'noDerivativesTrust200902'
+                          or @ipr = 'pre5378Trust200902'">
+            This Internet-Draft is submitted to IETF in full conformance with
+            the provisions of BCP 78 and BCP 79.
+          </xsl:when>
+          <xsl:otherwise>
+            CONFORMANCE UNDEFINED.
+          </xsl:otherwise>
+        </xsl:choose>
+
+        <!-- warn about iprExtract without effect -->
+        <xsl:if test="@iprExtract and (@ipr != 'noModification3667' and @ipr != 'noDerivatives3667' and @ipr != 'noModification3978' and @ipr != 'noDerivatives3978')">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg" select="concat('/rfc/@iprExtract does not have any effect for /rfc/@ipr=',@ipr)"/>
+          </xsl:call-template>
+        </xsl:if>
+
+        <!-- restrictions -->
+        <xsl:choose>
+          <xsl:when test="@ipr = 'noModificationTrust200811'">
+            <xsl:value-of select="$noModificationTrust200811Clause"/>
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativesTrust200811'">
+            <xsl:value-of select="$noDerivativesTrust200___Clause"/>
+          </xsl:when>
+          <xsl:when test="@ipr = 'noModificationTrust200902'">
+            <xsl:value-of select="$noModificationTrust200902Clause"/>
+          </xsl:when>
+          <xsl:when test="@ipr = 'noDerivativesTrust200902'">
+            <xsl:value-of select="$noDerivativesTrust200___Clause"/>
+          </xsl:when>
+          <!-- escape clause moved to Copyright Notice as of 2009-11 -->
+          <xsl:when test="@ipr = 'pre5378Trust200902' and $pub-yearmonth &lt; 200911">
+            <xsl:value-of select="$pre5378EscapeClause"/>
+          </xsl:when>
+
+          <xsl:otherwise />
+        </xsl:choose>
+      </t>
+      <xsl:choose>
+        <xsl:when test="$id-boilerplate='2010'">
+          <t>
+            Internet-Drafts are working documents of the Internet Engineering
+            Task Force (IETF). Note that other groups may also distribute
+            working documents as Internet-Drafts. The list of current
+            Internet-Drafts is at <eref target='http://datatracker.ietf.org/drafts/current/'>http://datatracker.ietf.org/drafts/current/</eref>.
+          </t>
+        </xsl:when>
+        <xsl:otherwise>
+          <t>
+            Internet-Drafts are working documents of the Internet Engineering
+            Task Force (IETF), its areas, and its working groups.
+            Note that other groups may also distribute working documents as
+            Internet-Drafts.
+          </t>
+        </xsl:otherwise>
+      </xsl:choose>
+      <t>
+        Internet-Drafts are draft documents valid for a maximum of six months
+        and may be updated, replaced, or obsoleted by other documents at any time.
+        It is inappropriate to use Internet-Drafts as reference material or to cite
+        them other than as &#8220;work in progress&#8221;.
+      </t>
+      <xsl:if test="$id-boilerplate=''">
+        <t>
+          The list of current Internet-Drafts can be accessed at
+          <eref target='http://www.ietf.org/ietf/1id-abstracts.txt'>http://www.ietf.org/ietf/1id-abstracts.txt</eref>.
+        </t>
+        <t>
+          The list of Internet-Draft Shadow Directories can be accessed at
+          <eref target='http://www.ietf.org/shadow.html'>http://www.ietf.org/shadow.html</eref>.
+        </t>
+      </xsl:if>
+      <t>
+        This Internet-Draft will expire <xsl:call-template name="expirydate"><xsl:with-param name="in-prose" select="true()"/></xsl:call-template>.
+      </t>
+    </xsl:when>
+
+    <xsl:when test="@category='bcp' and $rfc-boilerplate='2010'">
+      <t>
+        This memo documents an Internet Best Current Practice.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='bcp'">
+      <t>
+        This document specifies an Internet Best Current Practices for the Internet
+        Community, and requests discussion and suggestions for improvements.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='exp' and $rfc-boilerplate='2010'">
+      <t>
+        This document is not an Internet Standards Track specification; it is
+        published for examination, experimental implementation, and evaluation.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='exp'">
+      <t>
+        This memo defines an Experimental Protocol for the Internet community.
+        It does not specify an Internet standard of any kind.
+        Discussion and suggestions for improvement are requested.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='historic' and $rfc-boilerplate='2010'">
+      <t>
+        This document is not an Internet Standards Track specification; it is
+        published for the historical record.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='historic'">
+      <t>
+        This memo describes a historic protocol for the Internet community.
+        It does not specify an Internet standard of any kind.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='std' and $rfc-boilerplate='2010'">
+      <t>
+        This is an Internet Standards Track document.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='std'">
+      <t>
+        This document specifies an Internet standards track protocol for the Internet
+        community, and requests discussion and suggestions for improvements.
+        Please refer to the current edition of the &#8220;Internet Official Protocol
+        Standards&#8221; (STD 1) for the standardization state and status of this
+        protocol. Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:when test="(@category='info' or not(@category)) and $rfc-boilerplate='2010'">
+      <t>
+        This document is not an Internet Standards Track specification; it is
+        published for informational purposes.
+      </t>
+    </xsl:when>
+    <xsl:when test="@category='info' or not(@category)">
+      <t>
+        This memo provides information for the Internet community.
+        It does not specify an Internet standard of any kind.
+        Distribution of this memo is unlimited.
+      </t>
+    </xsl:when>
+    <xsl:otherwise>
+      <t>
+        UNSUPPORTED CATEGORY.
+      </t>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unsupported value for /rfc/@category: ', @category)"/>
+        <xsl:with-param name="inline" select="'no'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <!-- 2nd and 3rd paragraph -->
+  <xsl:if test="$rfc-boilerplate='2010' and @number">
+    <t>
+      <xsl:if test="@category='exp'">
+        This document defines an Experimental Protocol for the Internet
+        community.
+      </xsl:if>
+      <xsl:if test="@category='historic'">
+        This document defines a Historic Document for the Internet community.
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="$submissionType='IETF'">
+          This document is a product of the Internet Engineering Task Force
+          (IETF).
+          <xsl:choose>
+            <xsl:when test="$consensus='yes'">
+              It represents the consensus of the IETF community.  It has
+              received public review and has been approved for publication by
+              the Internet Engineering Steering Group (IESG).
+            </xsl:when>
+            <xsl:otherwise>
+              It has been approved for publication by the Internet Engineering
+              Steering Group (IESG).
+              <!-- sanity check of $consensus -->
+              <xsl:if test="@category='std' or @category='bcp'">
+                <xsl:call-template name="error">
+                  <xsl:with-param name="msg" select="'IETF BCPs and Standards Track documents require IETF consensus, check values of @category and @consensus!'"/>
+                  <xsl:with-param name="inline" select="'no'"/>
+                </xsl:call-template>
+              </xsl:if>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="$submissionType='IAB'">
+          This document is a product of the Internet Architecture Board (IAB)
+          and represents information that the IAB has deemed valuable to
+          provide for permanent record.
+          <xsl:if test="$consensus='yes'">
+            It represents the consensus of the Internet Architecture Board (IAB).
+          </xsl:if>
+        </xsl:when>
+        <xsl:when test="$submissionType='IRTF'">
+          This document is a product of the Internet Research Task Force (IRTF).
+          The IRTF publishes the results of Internet-related research and
+          development activities.  These results might not be suitable for
+          deployment.
+          <xsl:choose>
+            <xsl:when test="$consensus='yes' and front/workgroup!=''">
+              This RFC represents the consensus of the
+              <xsl:value-of select="front/workgroup"/> Research Group of the Internet
+              Research Task Force (IRTF).
+            </xsl:when>
+            <xsl:when test="$consensus='no' and front/workgroup!=''">
+              This RFC represents the individual opinion(s) of one or more
+              members of the <xsl:value-of select="front/workgroup"/> Research Group of the
+              Internet Research Task Force (IRTF).
+            </xsl:when>
+            <xsl:otherwise>
+              <!-- no research group -->
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="$submissionType='independent'">
+          This is a contribution to the RFC Series, independently of any other
+          RFC stream.  The RFC Editor has chosen to publish this document at
+          its discretion and makes no statement about its value for
+          implementation or deployment.
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- will contain error message already -->
+          <xsl:value-of select="$submissionType"/>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:choose>
+        <xsl:when test="$submissionType='IETF'">
+          <xsl:choose>
+            <xsl:when test="@category='bcp'">
+              Further information on BCPs is available in <xsl:value-of select="$hab-reference"/>.
+            </xsl:when>
+            <xsl:when test="@category='std'">
+              Further information on Internet Standards is available in <xsl:value-of select="$hab-reference"/>.
+            </xsl:when>
+            <xsl:otherwise>
+              Not all documents approved by the IESG are a candidate for any
+              level of Internet Standard; see <xsl:value-of select="$hab-reference"/>.
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:variable name="approver">
+            <xsl:choose>
+              <xsl:when test="$submissionType='IAB'">IAB</xsl:when>
+              <xsl:when test="$submissionType='IRTF'">IRSG</xsl:when>
+              <xsl:otherwise>RFC Editor</xsl:otherwise>
+            </xsl:choose>
+          </xsl:variable>
+
+          Documents approved for publication by the
+          <xsl:value-of select="$approver"/> are not a candidate for any level
+          of Internet Standard; see <xsl:value-of select="$hab-reference"/>.
+        </xsl:otherwise>
+      </xsl:choose>
+    </t>
+    <t>
+      Information about the current status of this document, any errata, and
+      how to provide feedback on it may be obtained at
+      <eref target="http://www.rfc-editor.org/info/rfc{@number}">http://www.rfc-editor.org/info/rfc<xsl:value-of select="@number"/></eref>.
+    </t>
+  </xsl:if>
+
+  </section>
+
+  <!-- some notes might go here; see http://www.rfc-editor.org/rfc-style-guide/rfc-style -->
+  <xsl:copy-of select="$notes"/>
+
+  <xsl:choose>
+    <xsl:when test="$ipr-2008-11">
+      <section anchor="{$anchor-pref}copyrightnotice">
+        <name>Copyright Notice</name>
+        <t>
+          Copyright &#169; <xsl:value-of select="$xml2rfc-ext-pub-year" /> IETF Trust and the persons identified
+          as the document authors.  All rights reserved.
+        </t>
+        <xsl:choose>
+          <xsl:when test="$ipr-2010-01">
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>)
+              in effect on the date of publication of this document. Please
+              review these documents carefully, as they describe your rights
+              and restrictions with respect to this document.
+              <xsl:if test="$submissionType='IETF'">
+                Code Components extracted from this document must include
+                Simplified BSD License text as described in Section 4.e of the
+                Trust Legal Provisions and are provided without warranty as
+                described in the Simplified BSD License.
+              </xsl:if>
+            </t>
+          </xsl:when>
+          <xsl:when test="$ipr-2009-09">
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>)
+              in effect on the date of publication of this document. Please
+              review these documents carefully, as they describe your rights
+              and restrictions with respect to this document. Code Components
+              extracted from this document must include Simplified BSD License
+              text as described in Section 4.e of the Trust Legal Provisions
+              and are provided without warranty as described in the BSD License.
+            </t>
+          </xsl:when>
+          <xsl:when test="$ipr-2009-02">
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents in effect on the date of
+              publication of this document
+              (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>).
+              Please review these documents carefully, as they describe your rights and restrictions with
+              respect to this document.
+            </t>
+          </xsl:when>
+          <xsl:otherwise>
+            <t>
+              This document is subject to BCP 78 and the IETF Trust's Legal
+              Provisions Relating to IETF Documents
+              (<eref target="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</eref>) in effect on the date of
+              publication of this document.  Please review these documents
+              carefully, as they describe your rights and restrictions with respect
+              to this document.
+            </t>
+          </xsl:otherwise>
+        </xsl:choose>
+
+        <!-- add warning for incompatible IPR attribute on RFCs -->
+        <xsl:variable name="stds-rfc-compatible-ipr"
+                      select="@ipr='pre5378Trust200902' or @ipr='trust200902' or @ipr='trust200811' or @ipr='full3978' or @ipr='full3667' or @ipr='full2026'"/>
+
+        <xsl:variable name="rfc-compatible-ipr"
+                      select="$stds-rfc-compatible-ipr or @ipr='noModificationTrust200902' or @ipr='noDerivativesTrust200902' or @ipr='noModificationTrust200811' or @ipr='noDerivativesTrust200811'"/>
+                      <!-- TODO: may want to add more historic variants -->
+
+        <xsl:variable name="is-stds-track"
+                      select="$submissionType='IETF' and @category='std'"/>
+
+        <xsl:variable name="status-diags">
+          <xsl:choose>
+            <xsl:when test="$is-stds-track and @number and @ipr and not($stds-rfc-compatible-ipr)">
+              <xsl:value-of select="concat('The /rfc/@ipr attribute value of ',@ipr,' is not allowed on standards-track RFCs.')"/>
+            </xsl:when>
+            <xsl:when test="@number and @ipr and not($rfc-compatible-ipr)">
+              <xsl:value-of select="concat('The /rfc/@ipr attribute value of ',@ipr,' is not allowed on RFCs.')"/>
+            </xsl:when>
+            <xsl:otherwise/>
+          </xsl:choose>
+        </xsl:variable>
+
+        <xsl:choose>
+          <xsl:when test="$status-diags!=''">
+            <t>
+              <spanx><xsl:value-of select="$status-diags"/></spanx>
+            </t>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="$status-diags"/>
+              <xsl:with-param name="inline" select="'no'"/>
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:when test="(@number or $pub-yearmonth >= 200911) and @ipr = 'pre5378Trust200902'">
+          <!-- special case: RFC5378 escape applies to RFCs as well -->
+          <!-- for IDs historically in Status Of This Memo, over here starting 2009-11 -->
+            <t>
+              <xsl:value-of select="$pre5378EscapeClause"/>
+            </t>
+          </xsl:when>
+          <xsl:when test="not(@number)">
+            <!-- not an RFC, handled elsewhere -->
+          </xsl:when>
+          <xsl:when test="not(@ipr)">
+            <!-- no IPR value; done -->
+          </xsl:when>
+          <xsl:when test="@ipr='trust200902' or @ipr='trust200811' or @ipr='full3978' or @ipr='full3667' or @ipr='full2026'">
+            <!-- default IPR, allowed here -->
+          </xsl:when>
+          <xsl:when test="@ipr='noModificationTrust200811'">
+            <t>
+              <xsl:value-of select="$noModificationTrust200811Clause"/>
+            </t>
+          </xsl:when>
+          <xsl:when test="@ipr='noModificationTrust200902'">
+            <t>
+              <xsl:value-of select="$noModificationTrust200902Clause"/>
+            </t>
+          </xsl:when>
+          <xsl:when test="@ipr='noDerivativesTrust200902' or @ipr='noDerivativesTrust200811'">
+            <t>
+              <xsl:value-of select="$noDerivativesTrust200___Clause"/>
+            </t>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:variable name="msg" select="concat('unexpected value of /rfc/@ipr for this type of document: ',@ipr)"/>
+            <t>
+              <spanx><xsl:value-of select="$msg"/></spanx>
+            </t>
+            <xsl:call-template name="error">
+              <xsl:with-param name="msg" select="$msg"/>
+              <xsl:with-param name="inline" select="'no'"/>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+
+      </section>
+    </xsl:when>
+    <xsl:when test="$ipr-2007-08">
+      <!-- no copyright notice -->
+    </xsl:when>
+    <xsl:when test="$ipr-rfc4748">
+      <section anchor="{$anchor-pref}copyrightnotice">
+        <name>Copyright Notice</name>
+        <t>
+          Copyright &#169; The IETF Trust (<xsl:value-of select="$xml2rfc-ext-pub-year" />).  All Rights Reserved.
+        </t>
+      </section>
+    </xsl:when>
+    <xsl:otherwise>
+      <section anchor="{$anchor-pref}copyrightnotice">
+        <name>Copyright Notice</name>
+        <t>
+          Copyright &#169; The Internet Society (<xsl:value-of select="$xml2rfc-ext-pub-year" />).  All Rights Reserved.
+        </t>
+      </section>
+    </xsl:otherwise>
+  </xsl:choose>
+</boilerplate>
+
+</xsl:template>
+
+<!-- TOC generation -->
+
+<xsl:template match="/" mode="toc">
+  <hr class="{$css-noprint}"/>
+
+  <nav id="{$anchor-pref}toc">
+    <xsl:call-template name="insert-errata">
+      <xsl:with-param name="section" select="'toc'"/>
+    </xsl:call-template>
+
+    <h2 class="np"> <!-- this pagebreak occurs always -->
+      <a href="#{$anchor-pref}toc">Table of Contents</a>
+    </h2>
+
+    <ul class="toc">
+      <xsl:apply-templates mode="toc" />
+    </ul>
+  </nav>
+</xsl:template>
+
+<xsl:template name="insert-toc-line">
+  <xsl:param name="number" />
+  <xsl:param name="target" />
+  <xsl:param name="title" />
+  <xsl:param name="name" />
+  <xsl:param name="tocparam" />
+  <xsl:param name="oldtitle" />
+  <xsl:param name="waschanged" />
+
+  <xsl:variable name="depth">
+    <!-- count the dots -->
+    <xsl:choose>
+      <xsl:when test="starts-with($number,'unnumbered-')">
+        <xsl:value-of select="string-length(translate(substring-after($number,'unnumbered-'),'.ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890&#167;','.'))"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="string-length(translate($number,'.ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890&#167;','.'))"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <!-- handle tocdepth parameter -->
+  <xsl:choose>
+    <xsl:when test="(not($tocparam) or $tocparam='' or $tocparam='default') and $depth >= $parsedTocDepth">
+      <!-- dropped entry because excluded -->
+      <xsl:attribute name="class">excluded</xsl:attribute>
+    </xsl:when>
+    <xsl:when test="$tocparam='exclude'">
+      <!-- dropped entry because excluded -->
+      <xsl:attribute name="class">excluded</xsl:attribute>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:choose>
+        <xsl:when test="starts-with($number,'del-')">
+          <del>
+            <xsl:value-of select="$number" />
+            <a href="#{$target}"><xsl:value-of select="$title"/></a>
+          </del>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:if test="$number != '' and not(contains($number,'unnumbered-'))">
+            <a href="#{$anchor-pref}section.{$number}">
+              <xsl:call-template name="emit-section-number">
+                <xsl:with-param name="no" select="$number"/>
+              </xsl:call-template>
+            </a>
+            <xsl:text>&#160;&#160;&#160;</xsl:text>
+          </xsl:if>
+          <a href="#{$target}">
+            <xsl:choose>
+              <xsl:when test="$waschanged!=''">
+                <ins><xsl:value-of select="$title"/></ins>
+                <del><xsl:value-of select="$oldtitle"/></del>
+              </xsl:when>
+              <xsl:when test="$name">
+                <xsl:call-template name="render-name-ref">
+                  <xsl:with-param name="n" select="$name/node()"/>
+                </xsl:call-template>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$title"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </a>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="back-toc">
+
+  <xsl:if test="//cref and $xml2rfc-comments='yes' and $xml2rfc-inline!='yes'">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'comments')"/>
+        <xsl:with-param name="title" select="'Editorial Comments'"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-ext-authors-section='before-appendices'">
+    <xsl:apply-templates select="/rfc/front" mode="toc" />
+  </xsl:if>
+  <xsl:apply-templates select="back/*[not(self::references)]" mode="toc" />
+
+  <!-- insert the index if index entries exist -->
+  <xsl:if test="$has-index">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'index')"/>
+        <xsl:with-param name="title" select="'Index'"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+  <xsl:if test="$xml2rfc-ext-authors-section='end'">
+    <xsl:apply-templates select="/rfc/front" mode="toc" />
+  </xsl:if>
+
+  <!-- copyright statements -->
+  <xsl:if test="$xml2rfc-private='' and not($no-copylong)">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'ipr')"/>
+        <xsl:with-param name="title" select="'Intellectual Property and Copyright Statements'"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template match="front" mode="toc">
+
+  <xsl:variable name="authors-title">
+    <xsl:call-template name="get-authors-section-title"/>
+  </xsl:variable>
+  <xsl:variable name="authors-number">
+    <xsl:call-template name="get-authors-section-number"/>
+  </xsl:variable>
+
+  <xsl:if test="$authors-number!='suppress' and $xml2rfc-authorship!='no'">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="target" select="concat($anchor-pref,'authors')"/>
+        <xsl:with-param name="title" select="$authors-title"/>
+        <xsl:with-param name="number" select="$authors-number"/>
+      </xsl:call-template>
+    </li>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="references-toc">
+
+  <!-- distinguish two cases: (a) single references element (process
+  as toplevel section; (b) multiple references sections (add one toplevel
+  container with subsection) -->
+
+  <xsl:variable name="refsecs" select="/rfc/back/references|/rfc/back/ed:replace/ed:ins/references"/>
+
+  <xsl:choose>
+    <xsl:when test="count($refsecs) = 0">
+      <!-- nop -->
+    </xsl:when>
+    <xsl:when test="count($refsecs) = 1">
+      <xsl:for-each select="$refsecs">
+        <xsl:variable name="title">
+          <xsl:choose>
+            <xsl:when test="@title!=''"><xsl:value-of select="@title" /></xsl:when>
+            <xsl:otherwise><xsl:value-of select="$xml2rfc-refparent"/></xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+
+        <li>
+          <xsl:call-template name="insert-toc-line">
+            <xsl:with-param name="number">
+              <xsl:call-template name="get-references-section-number"/>
+            </xsl:with-param>
+            <xsl:with-param name="target" select="concat($anchor-pref,'references')"/>
+            <xsl:with-param name="title" select="$title"/>
+            <xsl:with-param name="name" select="name"/>
+          </xsl:call-template>
+        </li>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:otherwise>
+      <li>
+        <!-- insert pseudo container -->
+        <xsl:call-template name="insert-toc-line">
+          <xsl:with-param name="number">
+            <xsl:call-template name="get-references-section-number"/>
+          </xsl:with-param>
+          <xsl:with-param name="target" select="concat($anchor-pref,'references')"/>
+          <xsl:with-param name="title" select="$xml2rfc-refparent"/>
+        </xsl:call-template>
+
+        <ul>
+          <!-- ...with subsections... -->
+          <xsl:for-each select="$refsecs">
+            <xsl:variable name="title">
+              <xsl:choose>
+                <xsl:when test="@title!=''"><xsl:value-of select="@title" /></xsl:when>
+                <xsl:otherwise><xsl:value-of select="$xml2rfc-refparent"/></xsl:otherwise>
+              </xsl:choose>
+            </xsl:variable>
+
+            <xsl:variable name="sectionNumber">
+              <xsl:call-template name="get-section-number" />
+            </xsl:variable>
+
+            <xsl:variable name="num">
+              <xsl:number level="any"/>
+            </xsl:variable>
+
+            <li>
+              <xsl:call-template name="insert-toc-line">
+                <xsl:with-param name="number" select="$sectionNumber"/>
+                <xsl:with-param name="target" select="concat($anchor-pref,'references','.',$num)"/>
+                <xsl:with-param name="title" select="$title"/>
+                <xsl:with-param name="name" select="name"/>
+              </xsl:call-template>
+            </li>
+          </xsl:for-each>
+        </ul>
+      </li>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="section|appendix" mode="toc">
+  <xsl:variable name="sectionNumber">
+    <xsl:call-template name="get-section-number" />
+  </xsl:variable>
+
+  <xsl:variable name="target">
+    <xsl:choose>
+      <xsl:when test="@anchor"><xsl:value-of select="@anchor" /></xsl:when>
+       <xsl:otherwise><xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$sectionNumber" /></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <!-- obtain content, just to check whether we need to recurse at all -->
+  <xsl:variable name="content">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="number" select="$sectionNumber"/>
+        <xsl:with-param name="target" select="$target"/>
+        <xsl:with-param name="title" select="@title"/>
+        <xsl:with-param name="name" select="name"/>
+        <xsl:with-param name="tocparam" select="@toc"/>
+        <xsl:with-param name="oldtitle" select="@ed:old-title"/>
+        <xsl:with-param name="waschanged" select="@ed:resolves"/>
+      </xsl:call-template>
+
+      <ul>
+        <xsl:apply-templates mode="toc" />
+      </ul>
+    </li>
+  </xsl:variable>
+
+  <xsl:if test="$content!=''">
+    <li>
+      <xsl:call-template name="insert-toc-line">
+        <xsl:with-param name="number" select="$sectionNumber"/>
+        <xsl:with-param name="target" select="$target"/>
+        <xsl:with-param name="title" select="@title"/>
+        <xsl:with-param name="name" select="name"/>
+        <xsl:with-param name="tocparam" select="@toc"/>
+        <xsl:with-param name="oldtitle" select="@ed:old-title"/>
+        <xsl:with-param name="waschanged" select="@ed:resolves"/>
+      </xsl:call-template>
+
+      <!-- obtain nested content, just to check whether we need to recurse at all -->
+      <xsl:variable name="nested-content">
+        <ul>
+          <xsl:apply-templates mode="toc" />
+        </ul>
+      </xsl:variable>
+
+      <!-- only recurse if we need to (do not produce useless list container) -->
+      <xsl:if test="$nested-content!=''">
+        <ul>
+          <xsl:apply-templates mode="toc" />
+        </ul>
+      </xsl:if>
+    </li>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="middle" mode="toc">
+  <xsl:apply-templates mode="toc" />
+  <xsl:call-template name="references-toc" />
+</xsl:template>
+
+<xsl:template match="rfc" mode="toc">
+  <xsl:apply-templates select="middle" mode="toc" />
+  <xsl:call-template name="back-toc" />
+</xsl:template>
+
+<xsl:template match="ed:del|ed:ins|ed:replace" mode="toc">
+  <xsl:apply-templates mode="toc" />
+</xsl:template>
+
+<xsl:template match="*|text()" mode="toc" />
+
+
+<xsl:template name="insertTocAppendix">
+
+  <xsl:if test="//figure[@title!='' or @anchor!='']">
+    <ul class="toc">
+      <li>Figures
+        <ul>
+          <xsl:for-each select="//figure[@title!='' or @anchor!='']">
+            <xsl:variable name="n"><xsl:call-template name="get-figure-number"/></xsl:variable>
+            <xsl:variable name="title">
+              <xsl:if test="not(starts-with($n,'u'))">
+                <xsl:text>Figure </xsl:text>
+                <xsl:value-of select="$n"/>
+                <xsl:if test="@title!=''">: </xsl:if>
+              </xsl:if>
+              <xsl:if test="@title"><xsl:value-of select="@title"/></xsl:if>
+            </xsl:variable>
+            <li>
+              <xsl:call-template name="insert-toc-line">
+                <xsl:with-param name="target" select="concat($anchor-pref,'figure.',$n)" />
+                <xsl:with-param name="title" select="$title" />
+              </xsl:call-template>
+            </li>
+          </xsl:for-each>
+        </ul>
+      </li>
+    </ul>
+  </xsl:if>
+
+  <!-- experimental -->
+  <xsl:if test="//ed:issue">
+    <xsl:call-template name="insertIssuesList" />
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template name="reference-name">
+  <xsl:param name="node" select="."/>
+
+  <xsl:for-each select="$node">
+    <xsl:choose>
+      <xsl:when test="$xml2rfc-symrefs!='no' and ancestor::ed:del">
+        <xsl:variable name="unprefixed" select="substring-after(@anchor,'deleted-')"/>
+        <xsl:choose>
+          <xsl:when test="$unprefixed!=''">
+            <xsl:value-of select="concat('[',$unprefixed,']')"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:if test="count(//reference[@anchor=current()/@anchor])!=1">
+              <xsl:message>Deleted duplicate anchors should have the prefix "deleted-": <xsl:value-of select="@anchor"/></xsl:message>
+            </xsl:if>
+            <xsl:value-of select="concat('[',@anchor,']')"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="$xml2rfc-symrefs!='no'">
+        <xsl:text>[</xsl:text>
+        <xsl:choose>
+          <xsl:when test="/rfc/back/displayreference[@target=current()/@anchor]">
+            <xsl:value-of select="/rfc/back/displayreference[@target=current()/@anchor]/@to"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="@anchor"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:text>]</xsl:text>
+      </xsl:when>
+      <xsl:when test="ancestor::ed:del">
+        <xsl:text>[del]</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>[<xsl:number level="any" count="reference[not(ancestor::ed:del)]"/>]</xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+
+
+<xsl:template name="replace-substring">
+  <xsl:param name="string" />
+  <xsl:param name="replace" />
+  <xsl:param name="by" />
+
+  <xsl:choose>
+    <xsl:when test="contains($string,$replace)">
+      <xsl:value-of select="concat(substring-before($string, $replace),$by)" />
+      <xsl:call-template name="replace-substring">
+        <xsl:with-param name="string" select="substring-after($string,$replace)" />
+        <xsl:with-param name="replace" select="$replace" />
+        <xsl:with-param name="by" select="$by" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="rfc-or-id-link">
+  <xsl:param name="name" />
+
+  <xsl:choose>
+    <xsl:when test="starts-with($name,'draft-')">
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-internet-draft-uri">
+          <xsl:with-param name="internet-draft" select="$name"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <a href="{$uri}"><xsl:value-of select="$name"/></a>
+      <xsl:call-template name="check-front-matter-ref">
+        <xsl:with-param name="name" select="$name"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="string(number($name))=$name">
+      <xsl:variable name="uri">
+        <xsl:call-template name="compute-rfc-uri">
+          <xsl:with-param name="rfc" select="$name"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <a href="{$uri}"><xsl:value-of select="$name"/></a>
+      <xsl:call-template name="check-front-matter-ref">
+        <xsl:with-param name="name" select="$name"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$name"/>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="concat('In metadata obsoletes/updates, RFC number of draft name is expected - found: ',$name)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="rfclist">
+  <xsl:param name="list" />
+  <xsl:choose>
+    <xsl:when test="contains($list,',')">
+      <xsl:variable name="rfcNo" select="substring-before($list,',')" />
+      <xsl:call-template name="rfc-or-id-link">
+        <xsl:with-param name="name" select="$rfcNo"/>
+      </xsl:call-template>
+      <xsl:text>, </xsl:text>
+      <xsl:call-template name="rfclist">
+        <xsl:with-param name="list" select="normalize-space(substring-after($list,','))" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="rfcNo" select="$list" />
+      <xsl:call-template name="rfc-or-id-link">
+        <xsl:with-param name="name" select="$rfcNo"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="check-front-matter-ref">
+  <xsl:param name="name"/>
+  <xsl:choose>
+    <xsl:when test="starts-with($name,'draft-')">
+      <xsl:if test="not(//references//reference//seriesInfo[@name='Internet-Draft' and @value=$name])">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('front matter mentions I-D ',$name,' for which there is no reference element')"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:if test="not(//references//reference//seriesInfo[@name='RFC' and @value=$name])">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg" select="concat('front matter mentions RFC ',$name,' for which there is no reference element')"/>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="check-anchor">
+  <xsl:if test="@anchor and @anchor!=''">
+    <!-- check validity of anchor name -->
+    <xsl:variable name="t" select="@anchor"/>
+    <xsl:variable name="tstart" select="substring($t,1,1)"/>
+
+    <!-- we only check for disallowed ASCII characters for now -->
+    <xsl:variable name="not-namestartchars">&#9;&#10;&#13;&#32;!"#$%&amp;'()*+,-./0123456789;&lt;=&gt;?@[\]^`[|}~</xsl:variable>
+
+    <xsl:if test="$tstart!=translate($tstart,$not-namestartchars,'')">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('anchor &quot;',$t,'&quot; can not start with character &quot;',$tstart,'&quot;')"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:call-template name="check-anchor-non-start">
+      <xsl:with-param name="f" select="$t"/>
+      <xsl:with-param name="t" select="$t"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="check-anchor-non-start">
+  <xsl:param name="f"/>
+  <xsl:param name="t"/>
+
+  <xsl:variable name="not-namechars">&#9;&#10;&#13;&#32;!"#$%&amp;'()*+,/;&lt;=&gt;?@[\]^`[|}~</xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$t=''">
+      <!-- Done -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="s" select="substring($t,1,1)"/>
+      <xsl:choose>
+        <xsl:when test="$s!=translate($s,$not-namechars,'')">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('anchor &quot;',$f,'&quot; contains invalid character &quot;',$s,'&quot; at position ',string-length($f) - string-length($t))"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="check-anchor-non-start">
+            <xsl:with-param name="f" select="$f"/>
+            <xsl:with-param name="t" select="substring($t,2)"/>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="sluggy-anchor">
+  <xsl:if test="self::section and (not(@anchor) or @anchor='')">
+    <xsl:variable name="fr">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.()-_ :%,/=@&lt;&gt;</xsl:variable>
+    <xsl:variable name="to">abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789.---_---------</xsl:variable>
+    <xsl:variable name="canslug" select="translate(normalize-space(concat(@title,name)),$fr,'')=''"/>
+    <xsl:if test="$canslug">
+      <xsl:variable name="slug" select="translate(normalize-space(concat(@title,name)),$fr,$to)"/>
+      <xsl:variable name="conflicts" select="//section[not(@anchor) and $slug=translate(normalize-space(concat(@title,name)),$fr,$to)]"/>
+      <xsl:choose>
+        <xsl:when test="count($conflicts)>1">
+          <xsl:variable name="c" select="preceding::*[not(@anchor) and $slug=translate(normalize-space(concat(@title,name)),$fr,$to)]"/>
+          <xsl:value-of select="concat('n-',$slug,'_',(1+count($c)))"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="concat('n-',$slug)"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="copy-anchor">
+  <xsl:call-template name="check-anchor"/>
+  <xsl:choose>
+    <xsl:when test="@anchor and @anchor!=''">
+      <xsl:attribute name="id"><xsl:value-of select="@anchor"/></xsl:attribute>
+    </xsl:when>
+    <xsl:when test="self::section">
+      <xsl:variable name="slug">
+        <xsl:call-template name="sluggy-anchor"/>
+      </xsl:variable>
+      <xsl:if test="$slug!=''">
+        <xsl:attribute name="id"><xsl:value-of select="$slug"/></xsl:attribute>
+      </xsl:if>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="rfclist-for-dcmeta">
+  <xsl:param name="list" />
+  <xsl:choose>
+    <xsl:when test="contains($list,',')">
+      <xsl:variable name="rfcNo" select="substring-before($list,',')" />
+      <meta name="dct.replaces" content="urn:ietf:rfc:{$rfcNo}" />
+      <xsl:call-template name="rfclist-for-dcmeta">
+        <xsl:with-param name="list" select="normalize-space(substring-after($list,','))" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="rfcNo" select="$list" />
+      <meta name="dct.replaces" content="urn:ietf:rfc:{$rfcNo}" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-paragraph-number">
+  <!-- no paragraph numbers in certain containers -->
+  <xsl:if test="not(ancestor::ul) and not(ancestor::dl) and not(ancestor::ol)">
+  
+    <xsl:choose>
+      <xsl:when test="parent::blockquote or parent::x:blockquote">
+        <!-- boilerplate -->
+        <xsl:for-each select="parent::blockquote|parent::x:blockquote"><xsl:call-template name="get-paragraph-number" />.</xsl:for-each>
+        <xsl:number count="t|x:blockquote|blockquote|x:note|aside|ul|dl|ol"/>
+      </xsl:when>
+  
+      <xsl:when test="parent::aside or parent::x:note">
+        <!-- boilerplate -->
+        <xsl:for-each select="parent::aside|parent::x:note"><xsl:call-template name="get-paragraph-number" />.</xsl:for-each>
+        <xsl:number count="t|x:blockquote|blockquote|x:note|aside|ul|dl|ol"/>
+      </xsl:when>
+
+      <xsl:when test="ancestor::section">
+        <!-- get section number of ancestor section element, then add t number -->
+        <xsl:for-each select="ancestor::section[1]"><xsl:call-template name="get-section-number" />.p.</xsl:for-each>
+        <xsl:variable name="b"><xsl:number count="t|x:blockquote|blockquote|x:note|aside|ul|dl|ol"/></xsl:variable>
+        <xsl:choose>
+          <xsl:when test="parent::section and ../@removeInRFC='true' and ../t[1]!=$section-removeInRFC">
+            <xsl:value-of select="1 + $b"/>
+          </xsl:when>
+          <xsl:otherwise><xsl:value-of select="$b"/></xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+  
+      <xsl:when test="ancestor::note">
+        <!-- or get section number of ancestor note element, then add t number -->
+        <xsl:for-each select="ancestor::note[1]"><xsl:call-template name="get-section-number" />.p.</xsl:for-each>
+        <xsl:variable name="b"><xsl:number count="t|x:blockquote|blockquote|x:note|aside|ul|dl|ol"/></xsl:variable>
+        <xsl:choose>
+          <xsl:when test="parent::note and ../@removeInRFC='true' and ../t[1]!=$note-removeInRFC">
+            <xsl:value-of select="1 + $b"/>
+          </xsl:when>
+          <xsl:otherwise><xsl:value-of select="$b"/></xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+  
+      <!-- abstract -->
+      <xsl:when test="ancestor::abstract">
+        <xsl:text>p.</xsl:text>
+        <xsl:number count="t|x:blockquote|blockquote|x:note|aside|ul|dl|ol"/>
+      </xsl:when>
+  
+      <xsl:otherwise/>
+    </xsl:choose>  
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="editingMark">
+  <xsl:if test="$xml2rfc-editing='yes' and ancestor::rfc">
+    <sup class="editingmark"><span><xsl:number level="any" count="postamble|preamble|t"/></span>&#0160;</sup>
+  </xsl:if>
+</xsl:template>
+
+<!-- internal ref support -->
+<xsl:key name="anchor-item-alias" match="//*[@anchor and (x:anchor-alias/@value or ed:replace/ed:ins/x:anchor-alias)]" use="x:anchor-alias/@value | ed:replace/ed:ins/x:anchor-alias/@value"/>
+
+<xsl:template match="x:ref">
+  <xsl:variable name="val" select="normalize-space(.)"/>
+  <xsl:variable name="target" select="key('anchor-item',$val) | key('anchor-item-alias',$val) | //reference/x:source[x:defines=$val]"/>
+  <xsl:if test="count($target)>1">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">internal link target for '<xsl:value-of select="."/>' is ambiguous; picking first.</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:choose>
+    <xsl:when test="$target[1]/@anchor">
+      <a href="#{$target[1]/@anchor}" class="smpl">
+        <xsl:call-template name="copy-anchor"/>
+        <!-- insert id when a backlink to this xref is needed in the index -->
+        <xsl:if test="//iref[@x:for-anchor=$val] | //iref[@x:for-anchor='' and ../@anchor=$val]">
+          <xsl:attribute name="id"><xsl:call-template name="compute-extref-anchor"/></xsl:attribute>
+        </xsl:if>
+        <xsl:value-of select="."/>
+      </a>
+    </xsl:when>
+    <xsl:when test="$target[1]/self::x:source">
+      <xsl:variable name="extdoc" select="document($target[1]/@href)"/>
+      <xsl:variable name="nodes" select="$extdoc//*[@anchor and (x:anchor-alias/@value=$val)]"/>
+      <xsl:if test="not($nodes)">
+        <xsl:call-template name="error">
+          <xsl:with-param name="msg">Anchor '<xsl:value-of select="$val"/>' not found in source file '<xsl:value-of select="$target[1]/@href"/>'.</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:variable name="t">
+        <xsl:call-template name="computed-auto-target">
+          <xsl:with-param name="bib" select="$target[1]/.."/>
+          <xsl:with-param name="ref" select="$nodes[1]"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <a href="{$t}" class="smpl">
+        <xsl:value-of select="."/>
+      </a>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">internal link target for '<xsl:value-of select="."/>' does not exist.</xsl:with-param>
+      </xsl:call-template>
+      <xsl:value-of select="."/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- Nothing to do here -->
+<xsl:template match="x:anchor-alias" />
+
+<!-- Quotes -->
+<xsl:template match="x:q">
+  <q>
+    <xsl:copy-of select="@cite"/>
+    <xsl:apply-templates/>
+  </q>
+</xsl:template>
+
+<!-- Notes -->
+<xsl:template match="x:note|aside">
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+
+  <div>
+    <xsl:if test="$p!='' and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <xsl:attribute name="id"><xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$p"/></xsl:attribute>
+    </xsl:if>
+    <aside>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:apply-templates select="*"/>
+    </aside>
+  </div>
+</xsl:template>
+
+<xsl:template match="x:bcp14|bcp14">
+  <!-- check valid BCP14 keywords, then emphasize them -->
+  <xsl:variable name="c" select="normalize-space(.)"/>
+  <xsl:choose>
+    <xsl:when test="$c='MUST' or $c='REQUIRED' or $c='SHALL'">
+      <em class="bcp14"><xsl:value-of select="."/></em>
+    </xsl:when>
+    <xsl:when test="$c='MUST NOT' or $c='SHALL NOT'">
+      <em class="bcp14"><xsl:value-of select="."/></em>
+    </xsl:when>
+    <xsl:when test="$c='SHOULD' or $c='RECOMMENDED'">
+      <em class="bcp14"><xsl:value-of select="."/></em>
+    </xsl:when>
+    <xsl:when test="$c='SHOULD NOT' or $c='NOT RECOMMENDED'">
+      <em class="bcp14"><xsl:value-of select="."/></em>
+    </xsl:when>
+    <xsl:when test="$c='MAY' or $c='OPTIONAL'">
+      <em class="bcp14"><xsl:value-of select="."/></em>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="."/>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('Unknown BCP14 keyword: ',.)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:blockquote|blockquote">
+  <xsl:variable name="p">
+    <xsl:call-template name="get-paragraph-number" />
+  </xsl:variable>
+  <div>
+    <xsl:call-template name="insertInsDelClass"/>
+    <xsl:call-template name="editingMark" />
+    <xsl:if test="string-length($p) &gt; 0 and not(ancestor::ed:del) and not(ancestor::ed:ins)">
+      <xsl:attribute name="id"><xsl:value-of select="$anchor-pref"/>section.<xsl:value-of select="$p"/></xsl:attribute>
+    </xsl:if>
+    <blockquote>
+      <xsl:call-template name="copy-anchor"/>
+      <xsl:copy-of select="@cite"/>
+      <xsl:choose>
+        <xsl:when test="t|ul|ol|dl|artwork|figure|sourcecode">
+          <xsl:apply-templates/>
+        </xsl:when>
+        <xsl:otherwise>
+          <p>
+            <xsl:apply-templates/>
+          </p>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:if test="@quotedFrom">
+        <cite>
+          <xsl:text>&#8212; </xsl:text>
+          <xsl:choose>
+            <xsl:when test="@cite"><a href="{@cite}"><xsl:value-of select="@quotedFrom"/></a></xsl:when>
+            <xsl:otherwise><xsl:value-of select="@quotedFrom"/></xsl:otherwise>
+          </xsl:choose>
+        </cite>
+      </xsl:if>
+    </blockquote>
+  </div>
+</xsl:template>
+
+<!-- Definitions -->
+<xsl:template match="x:dfn">
+  <dfn>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </dfn>
+</xsl:template>
+
+<!-- headings -->
+<xsl:template match="x:h">
+  <b>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </b>
+</xsl:template>
+
+<!-- superscripts -->
+<xsl:template match="x:sup|sup">
+  <sup>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </sup>
+</xsl:template>
+
+<!-- subscripts -->
+<xsl:template match="sub">
+  <sub>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </sub>
+</xsl:template>
+
+<!-- bold -->
+<xsl:template match="x:highlight">
+  <b>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates/>
+  </b>
+</xsl:template>
+
+<!-- measuring lengths -->
+<xsl:template match="x:length-of">
+  <xsl:variable name="target" select="//*[@anchor=current()/@target]"/>
+  <xsl:if test="count($target)!=1">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg" select="concat('@target ',@target,' defined ',count($target),' times.')"/>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:variable name="content">
+    <xsl:apply-templates select="$target"/>
+  </xsl:variable>
+  <xsl:variable name="lineends" select="string-length($content) - string-length(translate($content,'&#10;',''))"/>
+  <xsl:variable name="indents">
+    <xsl:choose>
+      <xsl:when test="@indented">
+        <xsl:value-of select="number(@indented) * $lineends"/>
+      </xsl:when>
+      <xsl:otherwise>0</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="string-length($content) + $lineends - $indents"/>
+</xsl:template>
+
+<!-- Almost Nop -->
+<xsl:template match="x:span">
+  <xsl:choose>
+    <xsl:when test="@x:lang and $prettyprint-class!=''">
+      <code class="{$prettyprint-class}">
+        <xsl:call-template name="copy-anchor"/>
+        <xsl:apply-templates/>
+      </code>
+    </xsl:when>
+    <xsl:otherwise>
+      <span>
+        <xsl:call-template name="copy-anchor"/>
+        <xsl:apply-templates/>
+      </span>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="x:parse-xml">
+  <xsl:apply-templates/>
+
+  <xsl:if test="function-available('exslt:node-set')">
+    <xsl:variable name="cleaned">
+      <xsl:apply-templates mode="cleanup-edits"/>
+    </xsl:variable>
+    <xsl:if test="$xml2rfc-ext-trace-parse-xml='yes'">
+      <xsl:call-template name="trace">
+        <xsl:with-param name="msg" select="concat('Parsing XML: ', $cleaned)"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="function-available('myns:parseXml')" use-when="function-available('myns:parseXml')">
+        <xsl:if test="myns:parseXml(concat($cleaned,''))!=''">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg" select="concat('Parse error in XML: ', myns:parseXml(concat($cleaned,'')))"/>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:when>
+      <xsl:when test="function-available('saxon:parse')" use-when="function-available('saxon:parse')">
+        <xsl:variable name="parsed" select="saxon:parse(concat($cleaned,''))"/>
+        <xsl:if test="$parsed='foo'">
+          <xsl:comment>should not get here</xsl:comment>
+        </xsl:if>
+      </xsl:when>
+      <xsl:when test="false()"></xsl:when>
+      <xsl:otherwise></xsl:otherwise>
+    </xsl:choose>
+  </xsl:if>
+</xsl:template>
+
+<!-- inlined RDF support -->
+<xsl:template match="rdf:Description">
+  <!-- ignore -->
+</xsl:template>
+
+<!-- cleanup for ins/del -->
+
+<xsl:template match="comment()|@*" mode="cleanup-edits"><xsl:copy/></xsl:template>
+
+<xsl:template match="text()" mode="cleanup-edits"><xsl:copy/></xsl:template>
+
+<xsl:template match="/" mode="cleanup-edits">
+  <xsl:copy><xsl:apply-templates select="node()" mode="cleanup-edits" /></xsl:copy>
+</xsl:template>
+
+<xsl:template match="ed:del" mode="cleanup-edits"/>
+
+<xsl:template match="ed:replace" mode="cleanup-edits">
+  <xsl:apply-templates mode="cleanup-edits"/>
+</xsl:template>
+
+<xsl:template match="ed:ins" mode="cleanup-edits">
+  <xsl:apply-templates mode="cleanup-edits"/>
+</xsl:template>
+
+
+<!-- ABNF support -->
+<xsl:template name="to-abnf-char-sequence">
+  <xsl:param name="chars"/>
+
+  <xsl:variable name="asciistring">&#160; !"#$%&amp;'()*+,-./<xsl:value-of select="$digits"/>:;&lt;=>?@<xsl:value-of select="$ucase"/>[\]^_`<xsl:value-of select="$lcase"/>{|}~&#127;</xsl:variable> 
+  <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
+  
+  <xsl:variable name="c" select="substring($chars,1,1)"/>
+  <xsl:variable name="r" select="substring($chars,2)"/>
+  <xsl:variable name="pos" select="string-length(substring-before($asciistring,$c))"/>
+  
+  <xsl:choose>
+    <xsl:when test="$pos >= 1">
+      <xsl:variable name="ascii" select="$pos + 31"/>
+      <xsl:variable name="h" select="floor($ascii div 16)"/>
+      <xsl:variable name="l" select="floor($ascii mod 16)"/>
+      <xsl:value-of select="concat(substring($hex,1 + $h,1),substring($hex,1 + $l,1))"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>??</xsl:text>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="concat('unexpected character in ABNF char sequence: ',substring($chars,1,1))" />
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+  <xsl:if test="$r!=''">
+    <xsl:text>.</xsl:text>
+    <xsl:call-template name="to-abnf-char-sequence">
+      <xsl:with-param name="chars" select="$r"/>
+    </xsl:call-template>
+  </xsl:if>
+
+</xsl:template>
+
+<xsl:template match="x:abnf-char-sequence">
+  <xsl:choose>
+    <xsl:when test="substring(.,1,1) != '&quot;' or substring(.,string-length(.),1) != '&quot;'">
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'contents of x:abnf-char-sequence needs to be quoted.'" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>%x</xsl:text>
+      <xsl:call-template name="to-abnf-char-sequence">
+        <xsl:with-param name="chars" select="substring(.,2,string-length(.)-2)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- box drawing -->
+
+<!-- nop for alignment -->
+<xsl:template match="x:x"/>
+
+<!-- box (top) -->
+<xsl:template match="x:bt">
+  <xsl:text>&#x250c;</xsl:text>
+  <xsl:value-of select="translate(substring(.,2,string-length(.)-2),'-','&#x2500;')"/>
+  <xsl:text>&#x2510;</xsl:text>
+</xsl:template>
+
+<!-- box (center) -->
+<xsl:template match="x:bc">
+  <xsl:variable name="first" select="substring(.,1)"/>
+  <xsl:variable name="content" select="substring(.,2,string-length(.)-2)"/>
+  <xsl:variable name="is-delimiter" select="translate($content,'-','')=''"/>
+
+  <xsl:choose>
+    <xsl:when test="$is-delimiter">
+      <xsl:text>&#x251c;</xsl:text>
+      <xsl:value-of select="translate($content,'-','&#x2500;')"/>
+      <xsl:text>&#x2524;</xsl:text>
+    </xsl:when>
+    <xsl:when test="*">
+      <xsl:for-each select="node()">
+        <xsl:choose>
+          <xsl:when test="position()=1">
+            <xsl:text>&#x2502;</xsl:text>
+            <xsl:value-of select="substring(.,2)"/>
+          </xsl:when>
+          <xsl:when test="position()=last()">
+            <xsl:value-of select="substring(.,1,string-length(.)-1)"/>
+            <xsl:text>&#x2502;</xsl:text>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates select="."/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>&#x2502;</xsl:text>
+      <xsl:value-of select="$content"/>
+      <xsl:text>&#x2502;</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<!-- box (bottom) -->
+<xsl:template match="x:bb">
+  <xsl:text>&#x2514;</xsl:text>
+  <xsl:value-of select="translate(substring(.,2,string-length(.)-2),'-','&#x2500;')"/>
+  <xsl:text>&#x2518;</xsl:text>
+</xsl:template>
+
+<!-- author handling extensions -->
+<xsl:template match="x:include-author">
+  <xsl:for-each select="/*/front/author[@anchor=current()/@target]">
+    <xsl:apply-templates select="."/>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- boilerplate -->
+<xsl:template match="boilerplate">
+  <xsl:apply-templates/>
+</xsl:template>
+
+<!-- experimental annotation support -->
+
+<xsl:template match="ed:issueref">
+  <xsl:choose>
+    <xsl:when test=".=//ed:issue/@name">
+      <a href="#{$anchor-pref}issue.{.}">
+        <xsl:apply-templates/>
+      </a>
+    </xsl:when>
+    <xsl:when test="@href">
+      <a href="{@href}" id="{$anchor-pref}issue.{.}">
+        <xsl:apply-templates/>
+      </a>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">Dangling ed:issueref: <xsl:value-of select="."/></xsl:with-param>
+      </xsl:call-template>
+      <xsl:apply-templates/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="ed:issue">
+  <xsl:variable name="class">
+    <xsl:choose>
+      <xsl:when test="@status='closed'">closedissue</xsl:when>
+      <xsl:otherwise>openissue</xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <table class="{$class}">
+    <tr>
+      <td colspan="3">
+        <a id="{$anchor-pref}issue.{@name}">
+          <xsl:choose>
+            <xsl:when test="@status='closed'">
+              <xsl:attribute name="class">closed-issue</xsl:attribute>
+            </xsl:when>
+            <xsl:when test="@status='editor'">
+              <xsl:attribute name="class">editor-issue</xsl:attribute>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:attribute name="class">open-issue</xsl:attribute>
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>&#160;I&#160;</xsl:text>
+        </a>
+        <xsl:text>&#160;</xsl:text>
+        <xsl:choose>
+          <xsl:when test="@href">
+            <em><a href="{@href}"><xsl:value-of select="@name" /></a></em>
+          </xsl:when>
+          <xsl:when test="@alternate-href">
+            <em>[<a href="{@alternate-href}">alternate link</a>]</em>
+          </xsl:when>
+          <xsl:otherwise>
+            <em><xsl:value-of select="@name" /></em>
+          </xsl:otherwise>
+        </xsl:choose>
+        &#0160;
+        (type: <xsl:value-of select="@type"/>, status: <xsl:value-of select="@status"/>)
+      </td>
+    </tr>
+
+    <xsl:apply-templates select="ed:item"/>
+    <xsl:apply-templates select="ed:resolution"/>
+
+    <xsl:variable name="changes" select="//*[@ed:resolves=current()/@name or ed:resolves=current()/@name]" />
+    <xsl:if test="$changes">
+      <tr>
+        <td class="top" colspan="3">
+          Associated changes in this document:
+          <xsl:variable name="issue" select="@name"/>
+          <xsl:for-each select="$changes">
+            <a href="#{$anchor-pref}change.{$issue}.{position()}">
+              <xsl:variable name="label">
+                <xsl:call-template name="get-section-number"/>
+              </xsl:variable>
+              <xsl:choose>
+                <xsl:when test="$label!=''"><xsl:value-of select="$label"/></xsl:when>
+                <xsl:otherwise>&lt;<xsl:value-of select="concat('#',$anchor-pref,'change.',$issue,'.',position())"/>&gt;</xsl:otherwise>
+              </xsl:choose>
+            </a>
+            <xsl:if test="position()!=last()">, </xsl:if>
+          </xsl:for-each>
+          <xsl:text>.</xsl:text>
+        </td>
+      </tr>
+    </xsl:if>
+  </table>
+
+</xsl:template>
+
+<xsl:template match="ed:item">
+  <tr>
+    <td class="top">
+      <xsl:if test="@entered-by">
+        <a href="mailto:{@entered-by}?subject={/rfc/@docName},%20{../@name}">
+          <i><xsl:value-of select="@entered-by"/></i>
+        </a>
+      </xsl:if>
+    </td>
+    <td class="topnowrap">
+      <xsl:value-of select="@date"/>
+    </td>
+    <td class="top">
+      <xsl:apply-templates select="node()" mode="issuehtml"/>
+    </td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="ed:resolution">
+  <tr>
+    <td class="top">
+      <xsl:if test="@entered-by">
+        <a href="mailto:{@entered-by}?subject={/rfc/@docName},%20{../@name}"><i><xsl:value-of select="@entered-by"/></i></a>
+      </xsl:if>
+    </td>
+    <td class="topnowrap">
+      <xsl:value-of select="@datetime"/>
+    </td>
+    <td class="top">
+      <em>Resolution:</em>
+      <xsl:apply-templates select="node()" mode="issuehtml"/>
+    </td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="ed:annotation">
+  <em>
+    <xsl:apply-templates/>
+  </em>
+</xsl:template>
+
+<!-- special templates for handling XHTML in issues -->
+<xsl:template match="text()" mode="issuehtml">
+  <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template match="*|@*" mode="issuehtml">
+  <xsl:message terminate="yes">Unexpected node in issue HTML: <xsl:value-of select="name(.)"/></xsl:message>
+</xsl:template>
+
+<xsl:template match="xhtml:a|xhtml:b|xhtml:br|xhtml:cite|xhtml:del|xhtml:em|xhtml:i|xhtml:ins|xhtml:q|xhtml:pre|xhtml:tt" mode="issuehtml">
+  <xsl:element name="{local-name()}">
+    <xsl:apply-templates select="@*|node()" mode="issuehtml"/>
+  </xsl:element>
+</xsl:template>
+
+<xsl:template match="xhtml:p" mode="issuehtml">
+  <xsl:apply-templates select="node()" mode="issuehtml"/>
+  <br class="p"/>
+</xsl:template>
+
+<xsl:template match="xhtml:a/@href|xhtml:q/@cite" mode="issuehtml">
+  <xsl:attribute name="{local-name(.)}">
+    <xsl:value-of select="."/>
+  </xsl:attribute>
+</xsl:template>
+
+<xsl:template match="ed:issueref" mode="issuehtml">
+  <xsl:apply-templates select="."/>
+</xsl:template>
+
+<xsl:template match="ed:eref" mode="issuehtml">
+  <xsl:text>&lt;</xsl:text>
+  <a href="{.}"><xsl:value-of select="."/></a>
+  <xsl:text>&gt;</xsl:text>
+</xsl:template>
+
+<xsl:template name="insertIssuesList">
+
+  <h2 id="{$anchor-pref}issues-list" ><a href="#{$anchor-pref}issues-list">Issues list</a></h2>
+  <table>
+    <thead>
+      <tr>
+        <th>Id</th>
+        <th>Type</th>
+        <th>Status</th>
+        <th>Date</th>
+        <th>Raised By</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="//ed:issue">
+        <xsl:sort select="@status" />
+        <xsl:sort select="@name" />
+        <tr>
+          <td><a href="#{$anchor-pref}issue.{@name}"><xsl:value-of select="@name" /></a></td>
+          <td><xsl:value-of select="@type" /></td>
+          <td><xsl:value-of select="@status" /></td>
+          <td><xsl:value-of select="ed:item[1]/@date" /></td>
+          <td><a href="mailto:{ed:item[1]/@entered-by}?subject={/rfc/@docName},%20{@name}"><xsl:value-of select="ed:item[1]/@entered-by" /></a></td>
+        </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+
+</xsl:template>
+
+<xsl:template name="insert-diagnostics">
+
+  <!-- check anchor names -->
+
+  <xsl:variable name="badAnchors" select="//*[starts-with(@anchor,$anchor-pref)]" />
+  <xsl:if test="$badAnchors">
+    <xsl:variable name="text">
+      <xsl:text>The following anchor names may collide with internally generated anchors because of their prefix "</xsl:text>
+      <xsl:value-of select="$anchor-pref" />
+      <xsl:text>": </xsl:text>
+      <xsl:for-each select="$badAnchors">
+        <xsl:value-of select="@anchor"/>
+        <xsl:call-template name="lineno"/>
+        <xsl:if test="position()!=last()">, </xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg"><xsl:value-of select="normalize-space($text)"/></xsl:with-param>
+      <xsl:with-param name="lineno" select="false()"/>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="badV3Anchors" select="//*[substring(@anchor,2,1)='-' and translate(substring(@anchor,1,1),$lcase,'')='']" />
+  <xsl:if test="$badV3Anchors">
+    <xsl:variable name="text">
+      <xsl:text>The following anchor names may collide with internally generated anchors in XML2RFCV3 mode because: </xsl:text>
+      <xsl:for-each select="$badV3Anchors">
+        <xsl:value-of select="@anchor"/>
+        <xsl:call-template name="lineno"/>
+        <xsl:if test="position()!=last()">, </xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg"><xsl:value-of select="normalize-space($text)"/></xsl:with-param>
+      <xsl:with-param name="lineno" select="false()"/>
+    </xsl:call-template>
+  </xsl:if>
+
+  <!-- check ABNF syntax references -->
+  <xsl:if test="//artwork[@type='abnf2616' or @type='abnf7230']">
+    <xsl:if test="not(//reference//seriesInfo[@name='RFC' and (@value='2068' or @value='2616' or @value='7230')]) and not(//reference//seriesInfo[@name='Internet-Draft' and (starts-with(@value, 'draft-ietf-httpbis-p1-messaging-'))])">
+      <!-- check for draft-ietf-httpbis-p1-messaging- is for backwards compat -->
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">document uses HTTP-style ABNF syntax, but doesn't reference RFC 2068, RFC 2616, or RFC 7230.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="//artwork[@type='abnf']">
+    <xsl:if test="not(//reference//seriesInfo[@name='RFC' and (@value='2234' or @value='4234' or @value='5234')])">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">document uses ABNF syntax, but doesn't reference RFC 2234, 4234 or 5234.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:if>
+
+  <!-- check IDs -->
+  <xsl:variable name="badTargets" select="//xref[not(@target=//@anchor) and not(@target=exslt:node-set($includeDirectives)//@anchor) and not(ancestor::ed:del)]" />
+  <xsl:if test="$badTargets">
+    <xsl:variable name="text">
+      <xsl:text>The following target names do not exist: </xsl:text>
+      <xsl:for-each select="$badTargets">
+        <xsl:value-of select="@target"/>
+        <xsl:if test="not(@target)">(@target attribute missing)</xsl:if>
+        <xsl:call-template name="lineno"/>
+        <xsl:if test="position()!=last()">
+          <xsl:text>, </xsl:text>
+        </xsl:if>
+      </xsl:for-each>
+    </xsl:variable>
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg"><xsl:value-of select="$text"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+
+</xsl:template>
+
+<!-- special change mark support, not supported by RFC2629 yet -->
+
+<xsl:template match="@ed:*" />
+
+<xsl:template match="ed:del">
+  <xsl:call-template name="insert-issue-pointer"/>
+  <del>
+    <xsl:copy-of select="@*[namespace-uri()='']"/>
+    <xsl:if test="not(@title) and ancestor-or-self::*[@ed:entered-by] and @datetime">
+      <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates />
+  </del>
+</xsl:template>
+
+<xsl:template match="ed:ins">
+  <xsl:call-template name="insert-issue-pointer"/>
+  <ins>
+    <xsl:copy-of select="@*[namespace-uri()='']"/>
+    <xsl:if test="not(@title) and ancestor-or-self::*[@ed:entered-by] and @datetime">
+      <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates />
+  </ins>
+</xsl:template>
+
+<xsl:template name="insert-issue-pointer">
+  <xsl:param name="deleted-anchor"/>
+  <xsl:variable name="change" select="."/>
+  <xsl:for-each select="@ed:resolves|ed:resolves">
+    <xsl:variable name="resolves" select="."/>
+    <!-- need the right context node for proper numbering -->
+    <xsl:variable name="count"><xsl:for-each select=".."><xsl:number level="any" count="*[@ed:resolves=$resolves or ed:resolves=$resolves]" /></xsl:for-each></xsl:variable>
+    <xsl:variable name="total" select="count(//*[@ed:resolves=$resolves or ed:resolves=$resolves])" />
+    <xsl:variable name="id">
+      <xsl:value-of select="$anchor-pref"/>change.<xsl:value-of select="$resolves"/>.<xsl:value-of select="$count" />
+    </xsl:variable>
+    <xsl:choose>
+      <!-- block level? -->
+      <xsl:when test="not(ancestor::t) and not(ancestor::title) and not(ancestor::figure) and not($change/@ed:old-title)">
+        <div class="issuepointer {$css-noprint}">
+          <xsl:if test="not($deleted-anchor)">
+            <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+          </xsl:if>
+          <xsl:if test="$count > 1">
+            <a class="bg-issue" title="previous change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count - 1}">&#x2191;</a>
+          </xsl:if>
+          <a class="open-issue" href="#{$anchor-pref}issue.{$resolves}" title="resolves: {$resolves}">
+            <xsl:choose>
+              <xsl:when test="//ed:issue[@name=$resolves and @status='closed']">
+                <xsl:attribute name="class">closed-issue</xsl:attribute>
+              </xsl:when>
+              <xsl:when test="//ed:issue[@name=$resolves and @status='editor']">
+                <xsl:attribute name="class">editor-issue</xsl:attribute>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:attribute name="class">open-issue</xsl:attribute>
+              </xsl:otherwise>
+            </xsl:choose>
+            <xsl:text>&#160;I&#160;</xsl:text>
+          </a>
+          <xsl:if test="$count &lt; $total">
+            <a class="bg-issue" title="next change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count + 1}">&#x2193;</a>
+          </xsl:if>
+          <xsl:text>&#160;</xsl:text>
+        </div>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:if test="$count > 1">
+          <a class="bg-issue" title="previous change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count - 1}">&#x2191;</a>
+        </xsl:if>
+        <a title="resolves: {$resolves}" href="#{$anchor-pref}issue.{$resolves}">
+          <xsl:if test="not($deleted-anchor)">
+            <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
+          </xsl:if>
+          <xsl:choose>
+            <xsl:when test="//ed:issue[@name=$resolves and @status='closed']">
+              <xsl:attribute name="class">closed-issue <xsl:value-of select="$css-noprint"/></xsl:attribute>
+            </xsl:when>
+            <xsl:when test="//ed:issue[@name=$resolves and @status='editor']">
+              <xsl:attribute name="class">editor-issue <xsl:value-of select="$css-noprint"/></xsl:attribute>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:attribute name="class">open-issue <xsl:value-of select="$css-noprint"/></xsl:attribute>
+            </xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>&#160;I&#160;</xsl:text>
+        </a>
+        <xsl:if test="$count &lt; $total">
+          <a class="bg-issue" title="next change for {$resolves}" href="#{$anchor-pref}change.{$resolves}.{$count + 1}">&#x2193;</a>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="ed:replace">
+  <!-- we need to special-case things like lists and tables -->
+  <xsl:choose>
+    <xsl:when test="parent::list">
+      <xsl:apply-templates select="ed:del/node()" />
+      <xsl:apply-templates select="ed:ins/node()" />
+    </xsl:when>
+    <xsl:when test="parent::references">
+      <xsl:apply-templates select="ed:del/node()" />
+      <xsl:apply-templates select="ed:ins/node()" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:if test="@cite">
+        <a class="editor-issue" href="{@cite}" target="_blank" title="see {@cite}">
+          <xsl:text>&#160;i&#160;</xsl:text>
+        </a>
+      </xsl:if>
+      <xsl:call-template name="insert-issue-pointer"/>
+      <xsl:if test="ed:del">
+        <del>
+          <xsl:copy-of select="@*[namespace-uri()='']"/>
+          <xsl:if test="not(@title) and ancestor-or-self::xsl:template[@ed:entered-by] and @datetime">
+            <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+          </xsl:if>
+          <xsl:apply-templates select="ed:del/node()" />
+        </del>
+      </xsl:if>
+      <xsl:if test="ed:ins">
+        <ins>
+          <xsl:copy-of select="@*[namespace-uri()='']"/>
+          <xsl:if test="not(@title) and ancestor-or-self::*[@ed:entered-by] and @datetime">
+            <xsl:attribute name="title"><xsl:value-of select="concat(@datetime,', ',ancestor-or-self::*[@ed:entered-by][1]/@ed:entered-by)"/></xsl:attribute>
+          </xsl:if>
+          <xsl:apply-templates select="ed:ins/node()" />
+        </ins>
+      </xsl:if>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- convenience template for helping Mozilla (pre/ins inheritance problem) -->
+<xsl:template name="insertInsDelClass">
+  <xsl:if test="ancestor::ed:del">
+    <xsl:attribute name="class">del</xsl:attribute>
+  </xsl:if>
+  <xsl:if test="ancestor::ed:ins">
+    <xsl:attribute name="class">ins</xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+
+<xsl:template name="sectionnumberAndEdits">
+  <xsl:choose>
+    <xsl:when test="ancestor::ed:del">
+      <xsl:text>del-</xsl:text>
+      <xsl:number count="ed:del//section" level="any"/>
+    </xsl:when>
+    <xsl:when test="@x:fixed-section-number and @x:fixed-section-number!=''">
+      <xsl:value-of select="@x:fixed-section-number"/>
+    </xsl:when>
+    <xsl:when test="(@x:fixed-section-number and @x:fixed-section-number='') or @numbered='false'">
+      <xsl:text>unnumbered-</xsl:text>
+      <xsl:number count="section[@x:fixed-section-number='' or @numbered='false']" level="any"/>
+    </xsl:when>
+    <xsl:when test="self::section and parent::ed:ins and local-name(../..)='replace'">
+      <xsl:for-each select="../.."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+      <xsl:for-each select="..">
+        <xsl:if test="parent::ed:replace">
+          <xsl:for-each select="..">
+            <xsl:if test="parent::section">.</xsl:if>
+            <xsl:variable name="cnt" select="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+            <xsl:choose>
+              <xsl:when test="ancestor::back and not(ancestor::section)"><xsl:number format="A" value="$cnt"/></xsl:when>
+              <xsl:otherwise><xsl:value-of select="$cnt"/></xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </xsl:if>
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="self::section[parent::ed:ins]">
+      <xsl:for-each select="../.."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+      <xsl:for-each select="..">
+        <xsl:if test="parent::section">.</xsl:if><xsl:value-of select="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+      </xsl:for-each>
+    </xsl:when>
+    <xsl:when test="self::section">
+      <xsl:for-each select=".."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+      <xsl:if test="parent::section">.</xsl:if>
+      <xsl:choose>
+        <xsl:when test="parent::back">
+          <xsl:number format="A" value="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:number value="1+count(preceding-sibling::section|preceding-sibling::ed:ins/section|preceding-sibling::ed:replace/ed:ins/section)" />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="self::references">
+      <xsl:choose>
+        <xsl:when test="count(/*/back/references)+count(/*/back/ed:replace/ed:ins/references)=1"><xsl:call-template name="get-references-section-number"/></xsl:when>
+        <xsl:otherwise><xsl:call-template name="get-references-section-number"/>.<xsl:number level="any"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="self::middle or self::back"><!-- done --></xsl:when>
+    <xsl:otherwise>
+      <!-- go up one level -->
+      <xsl:for-each select=".."><xsl:call-template name="sectionnumberAndEdits" /></xsl:for-each>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- utilities for warnings -->
+
+<xsl:template name="trace">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="inline"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">TRACE</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="$inline"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="inline-warning">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">WARNING</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="'yes'"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="warning">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">WARNING</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="'no'"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="info">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">INFO</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="'no'"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="error">
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="inline"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:call-template name="emit-message">
+    <xsl:with-param name="level">ERROR</xsl:with-param>
+    <xsl:with-param name="msg" select="$msg"/>
+    <xsl:with-param name="msg2" select="$msg2"/>
+    <xsl:with-param name="inline" select="$inline"/>
+    <xsl:with-param name="lineno" select="$lineno"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emit-message">
+  <xsl:param name="level"/>
+  <xsl:param name="msg"/>
+  <xsl:param name="msg2"/>
+  <xsl:param name="inline"/>
+  <xsl:param name="lineno" select="true()"/>
+  <xsl:variable name="message"><xsl:value-of select="$level"/>: <xsl:value-of select="$msg"/><xsl:if test="$msg2!=''"> - <xsl:value-of select="$msg2"/></xsl:if><xsl:if test="$lineno"><xsl:call-template name="lineno"/></xsl:if></xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$inline!='no'">
+      <xsl:choose>
+        <xsl:when test="ancestor::t">
+          <span class="{$css-error}"><xsl:value-of select="$message"/></span>
+        </xsl:when>
+        <xsl:otherwise>
+          <div class="{$css-error}"><xsl:value-of select="$message"/></div>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <!-- this fails when the message contains characters not encodable in the output encoding -->
+      <!-- <xsl:comment><xsl:value-of select="$message"/></xsl:comment> -->
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:message><xsl:value-of select="$message"/></xsl:message>
+</xsl:template>
+
+<!-- table formatting -->
+
+<xsl:template match="table">
+  <div>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="iref"/>
+    <table>
+      <xsl:if test="name or @anchor!=''">
+        <xsl:variable name="n"><xsl:call-template name="get-table-number"/></xsl:variable>
+        <caption>
+          <xsl:if test="not(starts-with($n,'u'))">
+            <xsl:text>Table </xsl:text>
+            <xsl:value-of select="$n"/>
+            <xsl:if test="name">: </xsl:if>
+          </xsl:if>
+          <xsl:if test="name">
+            <xsl:apply-templates select="name/node()"/>
+          </xsl:if>
+        </caption>
+      </xsl:if>
+      <xsl:apply-templates select="*[not(self::iref)]"/>
+    </table>
+  </div>
+</xsl:template>
+
+<xsl:template match="table/name"/>
+
+<xsl:template match="tbody">
+  <tbody>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </tbody>
+</xsl:template>
+
+<xsl:template match="tfoot">
+  <tfoot>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </tfoot>
+</xsl:template>
+
+<xsl:template match="thead">
+  <thead>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </thead>
+</xsl:template>
+
+<xsl:template match="tr">
+  <tr>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:apply-templates select="*"/>
+  </tr>
+</xsl:template>
+
+<xsl:template name="t-alignment">
+  <xsl:if test="@align and @align!=''">
+    <xsl:attribute name="class">
+      <xsl:choose>
+        <xsl:when test="@align='left'"><xsl:value-of select="$css-left"/></xsl:when>
+        <xsl:when test="@align='right'"><xsl:value-of select="$css-right"/></xsl:when>
+        <xsl:when test="@align='center'">center</xsl:when>
+        <xsl:otherwise>
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">Unknown align attribute: <xsl:value-of select="@align"/></xsl:with-param>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match="td">
+  <td>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="t-alignment"/>
+    <xsl:copy-of select="@colspan|@rowspan"/>
+    <xsl:apply-templates select="node()"/>
+  </td>
+</xsl:template>
+
+<xsl:template match="th">
+  <th>
+    <xsl:call-template name="copy-anchor"/>
+    <xsl:call-template name="t-alignment"/>
+    <xsl:copy-of select="@colspan|@rowspan"/>
+    <xsl:apply-templates select="node()"/>
+  </th>
+</xsl:template>
+
+<xsl:template match="td/br|th/br">
+  <br/>
+</xsl:template>
+
+<xsl:template match="texttable">
+  <xsl:call-template name="check-no-text-content"/>
+
+  <xsl:variable name="anch">
+    <xsl:call-template name="get-table-anchor"/>
+  </xsl:variable>
+
+  <div id="{$anch}" class="{$css-tt}">
+
+    <xsl:if test="@anchor!=''">
+      <div id="{@anchor}"/>
+    </xsl:if>
+    <xsl:apply-templates select="preamble" />
+
+    <xsl:variable name="style">
+      <xsl:value-of select="$css-tt"/>
+      <xsl:text> </xsl:text>
+      <xsl:choose>
+        <xsl:when test="@style!=''">
+          <xsl:value-of select="@style"/>
+        </xsl:when>
+        <xsl:otherwise>full</xsl:otherwise>
+      </xsl:choose>
+      <xsl:choose>
+        <xsl:when test="@align='left'"><xsl:text> </xsl:text><xsl:value-of select="$css-tleft"/></xsl:when>
+        <xsl:when test="@align='right'"><xsl:text> </xsl:text><xsl:value-of select="$css-tright"/></xsl:when>
+        <xsl:when test="@align='center' or not(@align) or @align=''"><xsl:text> </xsl:text><xsl:value-of select="$css-tcenter"/></xsl:when>
+        <xsl:otherwise/>
+      </xsl:choose>
+    </xsl:variable>
+
+    <table class="{$style}">
+      <xsl:if test="(@title!='') or (@anchor!='' and not(@suppress-title='true'))">
+        <xsl:variable name="n"><xsl:call-template name="get-table-number"/></xsl:variable>
+        <caption>
+          <xsl:if test="not(starts-with($n,'u'))">
+            <xsl:text>Table </xsl:text>
+            <xsl:value-of select="$n"/>
+            <xsl:if test="@title!=''">: </xsl:if>
+          </xsl:if>
+          <xsl:if test="@title!=''">
+            <xsl:value-of select="@title" />
+          </xsl:if>
+        </caption>
+      </xsl:if>
+
+      <xsl:if test="ttcol!=''">
+        <!-- skip header when all column titles are empty -->
+        <thead>
+          <tr>
+            <xsl:apply-templates select="ttcol" />
+          </tr>
+        </thead>
+      </xsl:if>
+      <tbody>
+        <xsl:variable name="columns" select="count(ttcol)" />
+        <xsl:variable name="fields" select="c | ed:replace/ed:ins/c | ed:replace/ed:del/c" />
+        <xsl:for-each select="$fields[$columns=1 or (position() mod $columns) = 1]">
+          <tr>
+            <xsl:for-each select=". | following-sibling::c[position() &lt; $columns]">
+              <td>
+                <xsl:call-template name="copy-anchor"/>
+                <xsl:call-template name="insertInsDelClass"/>
+                <xsl:variable name="pos" select="position()" />
+                <xsl:variable name="col" select="../ttcol[position() = $pos]" />
+                <xsl:choose>
+                  <xsl:when test="$col/@align='right' or $col/@align='center'">
+                    <xsl:attribute name="class"><xsl:value-of select="$col/@align"/></xsl:attribute>
+                  </xsl:when>
+                  <xsl:when test="$col/@align='left' or not($col/@align)">
+                    <xsl:attribute name="class"><xsl:value-of select="$css-left"/></xsl:attribute>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:call-template name="warning">
+                      <xsl:with-param name="msg">Unknown align attribute on ttcol: <xsl:value-of select="$col/@align"/></xsl:with-param>
+                    </xsl:call-template>
+                  </xsl:otherwise>
+                </xsl:choose>
+                <xsl:apply-templates select="node()" />
+              </td>
+            </xsl:for-each>
+          </tr>
+        </xsl:for-each>
+      </tbody>
+    </table>
+    <xsl:apply-templates select="postamble" />
+  </div>
+
+</xsl:template>
+
+<xsl:template match="ttcol">
+  <th>
+
+    <xsl:choose>
+      <xsl:when test="@align='right' or @align='center' or @align='left'">
+        <xsl:attribute name="class"><xsl:value-of select="@align"/></xsl:attribute>
+      </xsl:when>
+      <xsl:when test="not(@align)">
+        <!-- that's the default, nothing to do here -->
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:message>Unknown align attribute on ttcol: <xsl:value-of select="@align"/></xsl:message>
+      </xsl:otherwise>
+    </xsl:choose>
+
+    <xsl:if test="@width">
+      <xsl:attribute name="style">width: <xsl:value-of select="@width" />;</xsl:attribute>
+    </xsl:if>
+
+    <xsl:apply-templates />
+  </th>
+</xsl:template>
+
+<!-- cref support -->
+
+<xsl:template name="get-comment-name">
+  <xsl:choose>
+    <xsl:when test="@anchor">
+      <xsl:value-of select="@anchor"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$anchor-pref"/>
+      <xsl:text>comment.</xsl:text>
+      <xsl:number count="cref[not(@anchor)]" level="any"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="cref">
+  <xsl:if test="$xml2rfc-comments!='no'">
+    <xsl:variable name="cid">
+      <xsl:call-template name="get-comment-name"/>
+    </xsl:variable>
+
+    <span class="comment">
+      <xsl:choose>
+        <xsl:when test="$xml2rfc-inline='yes'">
+          <xsl:attribute name="id">
+            <xsl:value-of select="$cid"/>
+          </xsl:attribute>
+          <xsl:text>[</xsl:text>
+          <a href="#{$cid}" class="smpl">
+            <xsl:value-of select="$cid"/>
+          </a>
+          <xsl:text>: </xsl:text>
+          <xsl:apply-templates select="text()|eref|xref"/>
+          <xsl:if test="@source"> --<xsl:value-of select="@source"/></xsl:if>
+          <xsl:text>]</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:attribute name="title">
+            <xsl:if test="@source"><xsl:value-of select="@source"/>: </xsl:if>
+            <xsl:variable name="content">
+              <xsl:apply-templates select="text()|eref|xref"/>
+            </xsl:variable>
+            <xsl:value-of select="$content"/>
+          </xsl:attribute>
+          <xsl:text>[</xsl:text>
+          <a href="#{$cid}">
+            <xsl:value-of select="$cid"/>
+          </a>
+          <xsl:text>]</xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+    </span>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insertComments">
+
+  <xsl:call-template name="insert-conditional-hrule"/>
+
+  <h2>
+    <xsl:call-template name="insert-conditional-pagebreak"/>
+    <a id="{$anchor-pref}comments" href="#{$anchor-pref}comments">Editorial Comments</a>
+  </h2>
+
+  <dl>
+    <xsl:for-each select="//cref">
+      <xsl:variable name="cid">
+        <xsl:choose>
+          <xsl:when test="@anchor">
+            <xsl:value-of select="@anchor"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$anchor-pref"/>
+            <xsl:text>comment.</xsl:text>
+            <xsl:number count="cref[not(@anchor)]" level="any"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <dt id="{$cid}">
+        [<xsl:value-of select="$cid"/>]
+      </dt>
+      <dd>
+        <xsl:apply-templates select="node()"/>
+        <xsl:if test="@source"> --<xsl:value-of select="@source"/></xsl:if>
+      </dd>
+    </xsl:for-each>
+  </dl>
+</xsl:template>
+
+
+<!-- Chapter Link Generation -->
+
+<xsl:template match="*" mode="links"><xsl:apply-templates mode="links"/></xsl:template>
+<xsl:template match="text()" mode="links" />
+
+<xsl:template match="/*/middle//section[not(ancestor::section)]" mode="links">
+  <xsl:variable name="sectionNumber"><xsl:call-template name="get-section-number" /></xsl:variable>
+  <xsl:variable name="title">
+    <xsl:if test="$sectionNumber!='' and not(contains($sectionNumber,'unnumbered-'))">
+      <xsl:value-of select="$sectionNumber"/>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="name">
+        <xsl:variable name="hold">
+          <xsl:apply-templates select="name/node()"/>
+        </xsl:variable>
+        <xsl:value-of select="normalize-space($hold)"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <link rel="Chapter" title="{$title}" href="#{$anchor-pref}section.{$sectionNumber}"/>
+  <xsl:apply-templates mode="links" />
+</xsl:template>
+
+<xsl:template match="/*/back//section[not(ancestor::section)]" mode="links">
+  <xsl:variable name="sectionNumber"><xsl:call-template name="get-section-number" /></xsl:variable>
+  <xsl:variable name="title">
+    <xsl:if test="$sectionNumber!='' and not(contains($sectionNumber,'unnumbered-'))">
+      <xsl:value-of select="$sectionNumber"/>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    <xsl:choose>
+      <xsl:when test="name">
+        <xsl:variable name="hold">
+          <xsl:apply-templates select="name/node()"/>
+        </xsl:variable>
+        <xsl:value-of select="normalize-space($hold)"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@title"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <link rel="Appendix" title="{$title}" href="#{$anchor-pref}section.{$sectionNumber}"/>
+  <xsl:apply-templates mode="links" />
+</xsl:template>
+
+<xsl:template match="/*/back/references[position()=1]" mode="links">
+  <xsl:variable name="sectionNumber"><xsl:call-template name="get-references-section-number" /></xsl:variable>
+  <link rel="Chapter" href="#{$anchor-pref}section.{$sectionNumber}">
+    <xsl:choose>
+      <xsl:when test="@title and count(/*/back/references)=1">
+        <xsl:attribute name="title">
+          <xsl:call-template name="get-references-section-number"/>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="@title"/>
+        </xsl:attribute>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:attribute name="title">
+          <xsl:call-template name="get-references-section-number"/>
+          <xsl:text> </xsl:text>
+          <xsl:value-of select="$xml2rfc-refparent"/>
+        </xsl:attribute>
+      </xsl:otherwise>
+    </xsl:choose>
+  </link>
+</xsl:template>
+
+<!-- convenience templates -->
+
+<xsl:template name="get-author-summary">
+  <xsl:choose>
+    <xsl:when test="count(/rfc/front/author)=1">
+      <xsl:value-of select="/rfc/front/author[1]/@surname" />
+    </xsl:when>
+    <xsl:when test="count(/rfc/front/author)=2">
+      <xsl:value-of select="concat(/rfc/front/author[1]/@surname,' &amp; ',/rfc/front/author[2]/@surname)" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat(/rfc/front/author[1]/@surname,', et al.')" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-bottom-center">
+  <xsl:choose>
+    <xsl:when test="/rfc/@docName">
+      <!-- for IDs, use the expiry date -->
+      <xsl:text>Expires </xsl:text><xsl:call-template name="expirydate" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="get-category-long"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-category-long">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-footer!=''"><xsl:value-of select="$xml2rfc-footer" /></xsl:when>
+    <xsl:when test="$xml2rfc-private!=''"/> <!-- private draft, footer not set -->
+    <xsl:when test="/rfc/@category='bcp'">Best Current Practice</xsl:when>
+    <xsl:when test="/rfc/@category='historic'">Historic</xsl:when>
+    <xsl:when test="/rfc/@category='info' or not(/rfc/@category)">Informational</xsl:when>
+    <xsl:when test="/rfc/@category='std'">Standards Track</xsl:when>
+    <xsl:when test="/rfc/@category='exp'">Experimental</xsl:when>
+    <xsl:otherwise>(category unknown)</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-header-center">
+  <xsl:choose>
+    <xsl:when test="string-length(/rfc/front/title/@abbrev) &gt; 0">
+      <xsl:value-of select="/rfc/front/title/@abbrev" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates select="/rfc/front/title" mode="get-text-content" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-header-left">
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-header!=''"><xsl:value-of select="$xml2rfc-header" /></xsl:when>
+    <xsl:when test="$xml2rfc-private!=''"/> <!-- private draft, header not set -->
+    <xsl:when test="/rfc/@ipr and not(/rfc/@number)">Internet-Draft</xsl:when>
+    <xsl:otherwise>RFC <xsl:value-of select="/rfc/@number"/></xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-generator">
+  <xsl:variable name="gen">
+    <xsl:text>http://greenbytes.de/tech/webdav/rfc2629.xslt, </xsl:text>
+    <!-- when RCS keyword substitution in place, add version info -->
+    <xsl:if test="contains('$Revision: 1.912 $',':')">
+      <xsl:value-of select="concat('Revision ',normalize-space(translate(substring-after('$Revision: 1.912 $', 'Revision: '),'$','')),', ')" />
+    </xsl:if>
+    <xsl:if test="contains('$Date: 2017/05/31 14:04:25 $',':')">
+      <xsl:value-of select="concat(normalize-space(translate(substring-after('$Date: 2017/05/31 14:04:25 $', 'Date: '),'$','')),', ')" />
+    </xsl:if>
+    <xsl:value-of select="concat('XSLT vendor: ',system-property('xsl:vendor'),' ',system-property('xsl:vendor-url'))" />
+  </xsl:variable>
+  <xsl:value-of select="$gen" />
+</xsl:template>
+
+<xsl:template name="get-header-right">
+  <xsl:value-of select="concat($xml2rfc-ext-pub-month, ' ', $xml2rfc-ext-pub-year)" />
+</xsl:template>
+
+<xsl:template name="get-keywords">
+  <xsl:for-each select="/rfc/front/keyword">
+    <xsl:if test="contains(.,',')">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">keyword element appears to contain a comma-separated list, split into multiple elements instead.</xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:value-of select="normalize-space(.)" />
+    <xsl:if test="position()!=last()">, </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- get language from context node. nearest ancestor or return the default of "en" -->
+<xsl:template name="get-lang">
+  <xsl:choose>
+    <xsl:when test="ancestor-or-self::*[@xml:lang]"><xsl:value-of select="ancestor-or-self::*/@xml:lang" /></xsl:when>
+    <xsl:otherwise>en</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-section-number">
+  <xsl:variable name="anchor" select="@anchor"/>
+  <xsl:choose>
+    <xsl:when test="self::note">
+      <xsl:number count="note"/>
+    </xsl:when>
+    <xsl:when test="@x:fixed-section-number and @x:fixed-section-number!=''">
+      <xsl:value-of select="@x:fixed-section-number"/>
+    </xsl:when>
+    <xsl:when test="(@x:fixed-section-number and @x:fixed-section-number='') or @numbered='false'">
+      <xsl:text>unnumbered-</xsl:text>
+      <xsl:number count="section[@x:fixed-section-number='' or @numbered='false']" level="any"/>
+      <!-- checks -->
+      <xsl:if test="@numbered='false'">
+        <xsl:if test="ancestor::section or ancestor::section">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">Only top-level sections can be unnumbered</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="following-sibling::section[not(@numbered) or @numberer!='false'] or following-sibling::references">
+          <xsl:call-template name="error">
+            <xsl:with-param name="msg">Unnumbered section is followed by numbered sections</xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:if>
+    </xsl:when>
+    <xsl:when test="$has-edits or ancestor::*/@x:fixed-section-number">
+      <xsl:call-template name="sectionnumberAndEdits" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:choose>
+        <xsl:when test="self::references">
+          <xsl:choose>
+            <xsl:when test="count(/*/back/references)=1">
+              <xsl:call-template name="get-references-section-number"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:call-template name="get-references-section-number"/>.<xsl:number count="references"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:when>
+        <xsl:when test="self::reference">
+          <xsl:for-each select="parent::references">
+            <xsl:choose>
+              <xsl:when test="count(/*/back/references)=1">
+                <xsl:call-template name="get-references-section-number"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:call-template name="get-references-section-number"/>.<xsl:number count="references"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </xsl:when>
+        <xsl:when test="ancestor::reference">
+          <xsl:for-each select="ancestor::reference">
+            <xsl:call-template name="get-section-number"/>
+          </xsl:for-each>
+        </xsl:when>
+        <xsl:when test="ancestor::back"><xsl:number count="section|appendix" level="multiple" format="A.1.1.1.1.1.1.1" /></xsl:when>
+        <xsl:when test="self::appendix"><xsl:number count="appendix" level="multiple" format="A.1.1.1.1.1.1.1" /></xsl:when>
+        <xsl:otherwise><xsl:number count="section" level="multiple"/></xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!-- get the section number for the references section -->
+<xsl:template name="get-references-section-number">
+  <xsl:value-of select="count(/rfc/middle/section) + count(/rfc/middle/ed:replace/ed:ins/section) + 1"/>
+</xsl:template>
+
+<xsl:template name="emit-section-number">
+  <xsl:param name="no"/>
+  <xsl:value-of select="$no"/><xsl:if test="not(contains($no,'.')) or $xml2rfc-ext-sec-no-trailing-dots!='no'">.</xsl:if>
+</xsl:template>
+
+<xsl:template name="get-section-type">
+  <xsl:param name="prec" /> <!-- TODO: check this, it's unused -->
+  <xsl:choose>
+    <xsl:when test="ancestor::back">Appendix</xsl:when>
+    <xsl:otherwise>Section</xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-table-number">
+  <xsl:choose>
+    <xsl:when test="@anchor!=''">
+      <xsl:number level="any" count="texttable[@anchor!='']|table[@anchor!='']" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>u.</xsl:text>
+      <xsl:number level="any" count="texttable[not(@anchor) or @anchor='']|table[not(@anchor) or @anchor='']" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-table-anchor">
+  <xsl:value-of select="$anchor-pref"/>
+  <xsl:text>table.</xsl:text>
+  <xsl:call-template name="get-table-number"/>
+</xsl:template>
+
+<xsl:template name="get-figure-number">
+  <xsl:choose>
+    <xsl:when test="@anchor!=''">
+      <xsl:number level="any" count="figure[@anchor!='']" />
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>u.</xsl:text>
+      <xsl:number level="any" count="figure[not(@anchor) or @anchor='']" />
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="get-figure-anchor">
+  <xsl:value-of select="$anchor-pref"/>
+  <xsl:text>figure.</xsl:text>
+  <xsl:call-template name="get-figure-number"/>
+</xsl:template>
+
+<!-- reformat contents of author/@initials -->
+<xsl:template name="format-initials">
+  <xsl:variable name="normalized" select="normalize-space(@initials)"/>
+
+  <xsl:choose>
+    <xsl:when test="$normalized=''">
+      <!-- nothing to do -->
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="r">
+        <xsl:call-template name="t-format-initials">
+          <xsl:with-param name="remainder" select="$normalized"/>
+        </xsl:call-template>
+      </xsl:variable>
+    
+      <xsl:if test="$r!=@initials">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">@initials '<xsl:value-of select="@initials"/>': did you mean '<xsl:value-of select="$r"/>'?</xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    
+      <xsl:value-of select="$r"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="t-format-initials">
+  <xsl:param name="have"/>
+  <xsl:param name="remainder"/>
+
+  <xsl:variable name="first" select="substring($remainder,1,1)"/>
+  <xsl:variable name="prev" select="substring($have,string-length($have))"/>
+
+<!--<xsl:message>
+have: <xsl:value-of select="$have"/>
+remainder: <xsl:value-of select="$remainder"/>
+first: <xsl:value-of select="$first"/>
+prev: <xsl:value-of select="$prev"/>
+</xsl:message>-->
+
+  <xsl:choose>
+    <xsl:when test="$remainder='' and $prev!='.'">
+      <xsl:value-of select="concat($have,'.')"/>
+    </xsl:when>
+    <xsl:when test="$remainder=''">
+      <xsl:value-of select="$have"/>
+    </xsl:when>
+    <xsl:when test="$prev='.' and $first='.'">
+      <!-- repeating dots -->
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="$have"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <!-- missing dot before '-' -->
+<!--    <xsl:when test="$prev!='.' and $first='-'">
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="concat($have,'.-')"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:when>-->
+    <!-- missing space after '.' -->
+<!--    <xsl:when test="$prev='.' and $first!=' '">
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="concat($have,' ',$first)"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:when>-->
+    <xsl:otherwise>
+      <xsl:call-template name="t-format-initials">
+        <xsl:with-param name="have" select="concat($have,$first)"/>
+        <xsl:with-param name="remainder" select="substring($remainder,2)"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="truncate-initials">
+  <xsl:param name="initials"/>
+  <xsl:variable name="local-multiple-initials">
+    <xsl:call-template name="parse-pis">
+      <xsl:with-param name="nodes" select="../../processing-instruction('rfc')|../processing-instruction('rfc')|./processing-instruction('rfc')"/>
+      <xsl:with-param name="attr" select="'multiple-initials'"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:variable name="use-multiple-initials">
+    <xsl:choose>
+      <xsl:when test="$local-multiple-initials!=''">
+        <xsl:value-of select="$local-multiple-initials"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$xml2rfc-multiple-initials"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="normalize-space($initials)=''"/>
+    <xsl:when test="$use-multiple-initials='yes'">
+      <xsl:value-of select="$initials"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat(substring-before($initials,'.'),'.')"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="extract-normalized">
+  <xsl:param name="node" select="."/>
+  <xsl:param name="name"/>
+  <xsl:param name="ascii" select="false()"/>
+
+  <xsl:variable name="n">
+    <xsl:choose>
+      <xsl:when test="$ascii and $node/@ascii!=''">
+        <xsl:value-of select="$node/@ascii"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$node"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  
+  <xsl:variable name="text" select="normalize-space($n)"/>
+  <xsl:if test="string-length($n) != string-length($text)">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">excessive whitespace in <xsl:value-of select="$name"/>: '<xsl:value-of select="$n"/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="$text=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">missing text in <xsl:value-of select="$name"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:value-of select="$text"/>
+</xsl:template>
+
+<!-- checking for email element -->
+<xsl:template name="extract-email">
+  <xsl:variable name="email" select="normalize-space(.)"/>
+  <xsl:if test="contains($email,' ')">
+    <xsl:call-template name="error">
+      <xsl:with-param name="msg">whitespace in email address: '<xsl:value-of select="."/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:variable name="email2">
+    <xsl:choose>
+      <xsl:when test="starts-with($email,'mailto:')">
+        <xsl:call-template name="warning">
+          <xsl:with-param name="msg">email should not include URI scheme: '<xsl:value-of select="."/>'</xsl:with-param>
+        </xsl:call-template>
+        <xsl:value-of select="substring($email, 1 + string-length('mailto:'))"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$email"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+  <xsl:value-of select="$email2"/>
+</xsl:template>
+
+<!-- checking for uri element -->
+<xsl:template name="extract-uri">
+  <xsl:variable name="uri" select="normalize-space(.)"/>
+  <xsl:if test="string-length(.) != string-length($uri) or contains($uri,' ')">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">excessive whitespace in URI: '<xsl:value-of select="."/>'</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="$uri=''">
+    <xsl:call-template name="warning">
+      <xsl:with-param name="msg">URI is empty</xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+
+  <xsl:value-of select="$uri"/>
+</xsl:template>
+
+<xsl:template name="insert-conditional-pagebreak">
+  <xsl:if test="$xml2rfc-compact!='yes'">
+    <xsl:attribute name="class">np</xsl:attribute>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="insert-conditional-hrule">
+  <xsl:if test="$xml2rfc-compact!='yes'">
+    <hr class="{$css-noprint}" />
+  </xsl:if>
+</xsl:template>
+
+<!-- get text content from marked-up text -->
+
+<xsl:template match="text()" mode="get-text-content">
+  <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template match="*" mode="get-text-content">
+  <xsl:apply-templates mode="get-text-content"/>
+</xsl:template>
+
+<xsl:template match="ed:del" mode="get-text-content">
+</xsl:template>
+
+<!-- parsing of processing instructions -->
+<xsl:template name="parse-pis">
+  <xsl:param name="nodes"/>
+  <xsl:param name="attr"/>
+  <xsl:param name="sep"/>
+  <xsl:param name="ret"/>
+  <xsl:param name="default"/>
+  <xsl:param name="duplicate-warning" select="'yes'"/>
+
+  <xsl:choose>
+    <xsl:when test="count($nodes)=0">
+      <xsl:choose>
+        <xsl:when test="$ret!=''">
+          <xsl:value-of select="$ret"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$default"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="ret2">
+        <xsl:for-each select="$nodes[1]">
+          <xsl:call-template name="parse-one-pi">
+            <xsl:with-param name="str" select="."/>
+            <xsl:with-param name="attr" select="$attr"/>
+            <xsl:with-param name="sep" select="$sep"/>
+            <xsl:with-param name="ret" select="$ret"/>
+            <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+          </xsl:call-template>
+        </xsl:for-each>
+      </xsl:variable>
+
+      <xsl:call-template name="parse-pis">
+        <xsl:with-param name="nodes" select="$nodes[position()!=1]"/>
+        <xsl:with-param name="attr" select="$attr"/>
+        <xsl:with-param name="sep" select="$sep"/>
+        <xsl:with-param name="ret" select="$ret2"/>
+        <xsl:with-param name="default" select="$default"/>
+        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+      </xsl:call-template>
+
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="parse-one-pi">
+  <xsl:param name="str"/>
+  <xsl:param name="attr"/>
+  <xsl:param name="sep"/>
+  <xsl:param name="ret"/>
+  <xsl:param name="duplicate-warning"/>
+
+  <xsl:variable name="str2">
+    <xsl:call-template name="eat-leading-whitespace">
+      <xsl:with-param name="str" select="$str"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="$str2=''">
+      <!-- done -->
+      <xsl:value-of select="$ret"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="attrname" select="substring-before($str2,'=')"/>
+
+      <xsl:choose>
+        <xsl:when test="$attrname=''">
+          <xsl:call-template name="warning">
+            <xsl:with-param name="msg">bad PI syntax: <xsl:value-of select="$str2"/></xsl:with-param>
+          </xsl:call-template>
+          <xsl:value-of select="$ret"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:variable name="remainder" select="substring($str2,2+string-length($attrname))"/>
+          <xsl:choose>
+            <xsl:when test="string-length($remainder) &lt; 2">
+              <xsl:call-template name="warning">
+                <xsl:with-param name="msg">bad PI value syntax: <xsl:value-of select="$remainder"/></xsl:with-param>
+              </xsl:call-template>
+              <xsl:value-of select="$ret"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:variable name="rem">
+                <xsl:call-template name="eat-leading-whitespace">
+                  <xsl:with-param name="str" select="$remainder"/>
+                </xsl:call-template>
+              </xsl:variable>
+              <xsl:variable name="qchars">&apos;&quot;</xsl:variable>
+              <xsl:variable name="qchar" select="substring($rem,1,1)"/>
+              <xsl:variable name="rem2" select="substring($rem,2)"/>
+              <xsl:choose>
+                <xsl:when test="not(contains($qchars,$qchar))">
+                  <xsl:call-template name="warning">
+                    <xsl:with-param name="msg">pseudo-attribute value needs to be quoted: <xsl:value-of select="$rem"/></xsl:with-param>
+                  </xsl:call-template>
+                  <xsl:value-of select="$ret"/>
+                </xsl:when>
+                <xsl:when test="not(contains($rem2,$qchar))">
+                  <xsl:call-template name="warning">
+                    <xsl:with-param name="msg">unmatched quote in: <xsl:value-of select="$rem2"/></xsl:with-param>
+                  </xsl:call-template>
+                  <xsl:value-of select="$ret"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  <xsl:variable name="value" select="substring-before($rem2,$qchar)"/>
+
+                  <!-- check pseudo-attribute names -->
+                  <xsl:if test="name()='rfc-ext' and $attr='SANITYCHECK'">
+                    <xsl:choose>
+                      <xsl:when test="$attrname='allow-markup-in-artwork'"/>
+                      <xsl:when test="$attrname='authors-section'"/>
+                      <xsl:when test="$attrname='check-artwork-width'"/>
+                      <xsl:when test="$attrname='duplex'"/>
+                      <xsl:when test="$attrname='html-pretty-print'"/>
+                      <xsl:when test="$attrname='include-index'"/>
+                      <xsl:when test="$attrname='include-references-in-index'"/>
+                      <xsl:when test="$attrname='justification'"/>
+                      <xsl:when test="$attrname='paragraph-links'"/>
+                      <xsl:when test="$attrname='parse-xml-in-artwork'"/>
+                      <xsl:when test="$attrname='refresh-from'"/>
+                      <xsl:when test="$attrname='refresh-interval'"/>
+                      <xsl:when test="$attrname='refresh-xslt'"/>
+                      <xsl:when test="$attrname='sec-no-trailing-dots'"/>
+                      <xsl:when test="$attrname='trace-parse-xml'"/>
+                      <xsl:when test="$attrname='vspace-pagebreak'"/>
+                      <xsl:when test="$attrname='xml2rfc-backend'"/>
+                      <xsl:otherwise>
+                        <xsl:call-template name="warning">
+                          <xsl:with-param name="msg">unsupported rfc-ext pseudo-attribute '<xsl:value-of select="$attrname"/>'</xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:otherwise>
+                    </xsl:choose>
+                  </xsl:if>
+
+                  <xsl:if test="name()='rfc' and $attr='SANITYCHECK'">
+                    <xsl:choose>
+                      <xsl:when test="$attrname='authorship'"/>
+                      <xsl:when test="$attrname='comments'"/>
+                      <xsl:when test="$attrname='compact'"/>
+                      <xsl:when test="$attrname='docmapping'">
+                        <xsl:if test="$value!='yes'">
+                          <xsl:call-template name="warning">
+                            <xsl:with-param name="msg">the rfc docmapping pseudo-attribute with values other than 'yes' in not supported by this processor.</xsl:with-param>
+                          </xsl:call-template>
+                        </xsl:if>
+                      </xsl:when>
+                      <xsl:when test="$attrname='editing'"/>
+                      <xsl:when test="$attrname='footer'"/>
+                      <xsl:when test="$attrname='header'"/>
+                      <xsl:when test="$attrname='include'">
+                        <xsl:call-template name="warning">
+                          <xsl:with-param name="msg">the rfc include pseudo-attribute is only partially supported by this processor, see http://greenbytes.de/tech/webdav/rfc2629xslt/rfc2629xslt.html#examples.internalsubset for alternative syntax.</xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:when>
+                      <xsl:when test="$attrname='inline'"/>
+                      <xsl:when test="$attrname='iprnotified'"/>
+                      <xsl:when test="$attrname='linefile'"/>
+                      <xsl:when test="$attrname='linkmailto'"/>
+                      <xsl:when test="$attrname='multiple-initials'"/>
+                      <xsl:when test="$attrname='private'"/>
+                      <xsl:when test="$attrname='rfcedstyle'"/>
+                      <xsl:when test="$attrname='sortrefs'"/>
+                      <xsl:when test="$attrname='subcompact'"/>
+                      <xsl:when test="$attrname='strict'"/>
+                      <xsl:when test="$attrname='symrefs'"/>
+                      <xsl:when test="$attrname='toc'"/>
+                      <xsl:when test="$attrname='tocdepth'"/>
+                      <xsl:when test="$attrname='tocindent'">
+                        <xsl:if test="$value!='yes'">
+                          <xsl:call-template name="warning">
+                            <xsl:with-param name="msg">the rfc tocindent pseudo-attribute with values other than 'yes' in not supported by this processor.</xsl:with-param>
+                          </xsl:call-template>
+                        </xsl:if>
+                      </xsl:when>
+                      <xsl:otherwise>
+                        <xsl:call-template name="info">
+                          <xsl:with-param name="msg">unsupported rfc pseudo-attribute '<xsl:value-of select="$attrname"/>'</xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:otherwise>
+                    </xsl:choose>
+                  </xsl:if>
+
+                  <xsl:choose>
+                    <xsl:when test="$attrname != $attr">
+                      <!-- pseudo-attr does not match, continue -->
+                      <xsl:call-template name="parse-one-pi">
+                        <xsl:with-param name="str" select="substring($rem2, 2 + string-length($value))"/>
+                        <xsl:with-param name="attr" select="$attr"/>
+                        <xsl:with-param name="sep" select="$sep"/>
+                        <xsl:with-param name="ret" select="$ret"/>
+                        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+                      </xsl:call-template>
+                    </xsl:when>
+                    <xsl:when test="$sep='' and $ret!=''">
+                      <!-- pseudo-attr does match, but we only want one value -->
+                      <xsl:if test="$ret != $value and $duplicate-warning='yes'">
+                        <xsl:call-template name="warning">
+                          <xsl:with-param name="msg">duplicate pseudo-attribute <xsl:value-of select="$attr"/>, overwriting value <xsl:value-of select="$ret"/></xsl:with-param>
+                        </xsl:call-template>
+                      </xsl:if>
+                      <xsl:call-template name="parse-one-pi">
+                        <xsl:with-param name="str" select="substring($rem2, 2 + string-length($value))"/>
+                        <xsl:with-param name="attr" select="$attr"/>
+                        <xsl:with-param name="sep" select="$sep"/>
+                        <xsl:with-param name="ret" select="$value"/>
+                        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+                      </xsl:call-template>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      <!-- pseudo-attr does match -->
+                      <xsl:call-template name="parse-one-pi">
+                        <xsl:with-param name="str" select="substring($rem2, 2 + string-length($value))"/>
+                        <xsl:with-param name="attr" select="$attr"/>
+                        <xsl:with-param name="sep" select="$sep"/>
+                        <xsl:with-param name="duplicate-warning" select="$duplicate-warning"/>
+                        <xsl:with-param name="ret">
+                          <xsl:choose>
+                            <xsl:when test="$ret!=''">
+                              <xsl:value-of select="concat($ret,$sep,$value)"/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                              <xsl:value-of select="$value"/>
+                            </xsl:otherwise>
+                          </xsl:choose>
+                        </xsl:with-param>
+                      </xsl:call-template>
+                    </xsl:otherwise>
+
+                  </xsl:choose>
+
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="eat-leading-whitespace">
+  <xsl:param name="str"/>
+
+  <xsl:choose>
+    <xsl:when test="$str=''">
+    </xsl:when>
+    <xsl:when test="translate(substring($str,1,1),' &#10;&#13;&#9;',' ')=' '">
+      <xsl:call-template name="eat-leading-whitespace">
+        <xsl:with-param name="str" select="substring($str,2)"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$str"/>
+    </xsl:otherwise>
+  </xsl:choose>
+
+</xsl:template>
+
+<!-- diag support -->
+<xsl:template name="lineno">
+  <xsl:if test="function-available('saxon-old:line-number')" use-when="function-available('saxon-old:line-number')">
+    <xsl:if test="saxon-old:line-number() > 0">
+      <xsl:text> (at line </xsl:text>
+      <xsl:value-of select="saxon-old:line-number()"/>
+      <xsl:if test="function-available('saxon-old:systemId')">
+        <xsl:variable name="rootsys">
+          <xsl:for-each select="/*">
+            <xsl:value-of select="saxon-old:systemId()"/>
+          </xsl:for-each>
+        </xsl:variable>
+        <xsl:if test="$rootsys != saxon-old:systemId()">
+          <xsl:text> of </xsl:text>
+          <xsl:value-of select="saxon-old:systemId()"/>
+        </xsl:if>
+      </xsl:if>
+      <xsl:text>)</xsl:text>
+    </xsl:if>
+  </xsl:if>
+  <xsl:if test="function-available('saxon:line-number')" use-when="function-available('saxon:line-number')">
+    <xsl:if test="saxon:line-number() > 0">
+      <xsl:text> (at line </xsl:text>
+      <xsl:value-of select="saxon:line-number()"/>
+      <xsl:if test="function-available('saxon:systemId')">
+        <xsl:variable name="rootsys">
+          <xsl:for-each select="/*">
+            <xsl:value-of select="saxon:systemId()"/>
+          </xsl:for-each>
+        </xsl:variable>
+        <xsl:if test="$rootsys != saxon:systemId()">
+          <xsl:text> of </xsl:text>
+          <xsl:value-of select="saxon:systemId()"/>
+        </xsl:if>
+      </xsl:if>
+      <xsl:text>)</xsl:text>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+<!-- define exslt:node-set for msxml -->
+<msxsl:script language="JScript" implements-prefix="exslt">
+  this['node-set'] = function (x) {
+    return x;
+  }
+</msxsl:script>
+
+<!-- date handling -->
+
+<msxsl:script language="JScript" implements-prefix="date">
+  function twodigits(s) {
+    return s &lt; 10 ? "0" + s : s;
+  }
+
+  this['date-time'] = function (x) {
+    var now = new Date();
+    var offs = now.getTimezoneOffset();
+    return now.getFullYear() + "-"
+      + twodigits(1 + now.getMonth()) + "-"
+      + twodigits(now.getDate()) + "T"
+      + twodigits(now.getHours()) + ":"
+      + twodigits(now.getMinutes()) + ":"
+      + twodigits(now.getSeconds())
+      + (offs >= 0 ? "-" : "+")
+      + twodigits(Math.abs(offs) / 60) + ":"
+      + twodigits(Math.abs(offs) % 60);
+  }
+</msxsl:script>
+
+<xsl:variable name="current-year">
+  <xsl:choose>
+    <xsl:when test="function-available('date:date-time')" use-when="function-available('date:date-time')">
+      <xsl:value-of select="substring-before(date:date-time(),'-')"/>
+    </xsl:when>
+    <xsl:when test="function-available('current-date')">
+      <xsl:value-of select="substring-before(string(current-date()),'-')"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="current-month">
+  <xsl:choose>
+    <xsl:when test="function-available('date:date-time')" use-when="function-available('date:date-time')">
+      <xsl:value-of select="substring-before(substring-after(date:date-time(),'-'),'-')"/>
+    </xsl:when>
+    <xsl:when test="function-available('current-date')">
+      <xsl:value-of select="substring-before(substring-after(string(current-date()),'-'),'-')"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="current-day">
+  <xsl:choose>
+    <xsl:when test="function-available('date:date-time')" use-when="function-available('date:date-time')">
+      <xsl:value-of select="substring-after(substring-after(substring-before(date:date-time(),'T'),'-'),'-')"/>
+    </xsl:when>
+    <xsl:when test="function-available('current-dateTime')">
+      <xsl:value-of select="substring-after(substring-after(substring-before(string(current-dateTime()),'T'),'-'),'-')"/>
+    </xsl:when>
+    <xsl:otherwise/>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:variable name="may-default-dates">
+  <xsl:choose>
+    <xsl:when test="$current-year!='' and $current-month!='' and $current-day!=''">
+      <xsl:variable name="year-specified" select="/rfc/front/date/@year and /rfc/front/date/@year!=''"/>
+      <xsl:variable name="month-specified" select="/rfc/front/date/@month and /rfc/front/date/@month!=''"/>
+      <xsl:variable name="day-specified" select="/rfc/front/date/@day and /rfc/front/date/@day!=''"/>
+      <xsl:variable name="system-month">
+        <xsl:call-template name="get-month-as-name">
+          <xsl:with-param name="month" select="$current-month"/>
+        </xsl:call-template>
+      </xsl:variable>
+      <xsl:choose>
+        <xsl:when test="$year-specified and /rfc/front/date/@year!=$current-year">Specified year <xsl:value-of select="/rfc/front/date/@year"/> does not match system date (<xsl:value-of select="$current-year"/>)</xsl:when>
+        <xsl:when test="$month-specified and /rfc/front/date/@month!=$system-month">Specified month <xsl:value-of select="/rfc/front/date/@month"/> does not match system date (<xsl:value-of select="$system-month"/>)</xsl:when>
+        <xsl:when test="$day-specified and /rfc/front/date/@day!=$current-day">Specified day does not match system date</xsl:when>
+        <xsl:when test="not($year-specified) and ($month-specified or $day-specified)">Can't default year when month or day is specified</xsl:when>
+        <xsl:when test="not($month-specified) and $day-specified">Can't default month when day is specified</xsl:when>
+        <xsl:otherwise>yes</xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <!-- may, but won't -->
+    <xsl:otherwise>yes</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<xsl:param name="xml2rfc-ext-pub-year">
+  <xsl:choose>
+    <xsl:when test="/rfc/front/date/@year and /rfc/front/date/@year!=''">
+      <xsl:value-of select="/rfc/front/date/@year"/>
+    </xsl:when>
+    <xsl:when test="$current-year!='' and $may-default-dates='yes'">
+      <xsl:value-of select="$current-year"/>
+    </xsl:when>
+    <xsl:when test="$current-year!='' and $may-default-dates!='yes'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="$may-default-dates"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'/rfc/front/date/@year missing (and XSLT processor cannot compute the system date)'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-pub-month">
+  <xsl:choose>
+    <xsl:when test="/rfc/front/date/@month and /rfc/front/date/@month!=''">
+      <xsl:variable name="m" select="/rfc/front/date/@month"/>
+      <xsl:choose>
+        <xsl:when test="string(number($m))!='NaN' and number($m) &gt; 0 and number($m) &lt; 13">
+          <xsl:call-template name="get-month-as-name">
+            <xsl:with-param name="month" select="$m"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$m"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:when test="$current-month!='' and $may-default-dates='yes'">
+      <xsl:call-template name="get-month-as-name">
+        <xsl:with-param name="month" select="$current-month"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:when test="$current-month!='' and $may-default-dates!='yes'">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg" select="$may-default-dates"/>
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="error">
+        <xsl:with-param name="msg" select="'/rfc/front/date/@month missing (and XSLT processor cannot compute the system date)'"/>
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="pub-month-numeric">
+  <xsl:call-template name="get-month-as-num">
+    <xsl:with-param name="month" select="$xml2rfc-ext-pub-month" />
+  </xsl:call-template>
+</xsl:param>
+
+<xsl:param name="xml2rfc-ext-pub-day">
+  <xsl:choose>
+    <xsl:when test="/rfc/front/date/@day and /rfc/front/date/@day!=''">
+      <xsl:value-of select="/rfc/front/date/@day"/>
+    </xsl:when>
+    <xsl:when test="$current-day!='' and $may-default-dates='yes'">
+      <xsl:value-of select="$current-day"/>
+    </xsl:when>
+    <xsl:otherwise /> <!-- harmless, we just don't have it -->
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="pub-yearmonth">
+  <!-- year or 0000 -->
+  <xsl:choose>
+    <xsl:when test="$xml2rfc-ext-pub-year!=''">
+      <xsl:value-of select="format-number($xml2rfc-ext-pub-year,'0000')"/>
+    </xsl:when>
+    <xsl:otherwise>0000</xsl:otherwise>
+  </xsl:choose>
+  <!-- month or 00 -->
+  <xsl:choose>
+    <xsl:when test="number($pub-month-numeric) &gt; 0">
+      <xsl:value-of select="format-number($pub-month-numeric,'00')"/>
+    </xsl:when>
+    <xsl:otherwise>00</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- simple validation support -->
+
+<xsl:template match="*" mode="validate">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="@*" mode="validate"/>
+
+<xsl:template name="warninvalid">
+  <xsl:param name="additionalDiagnostics"/>
+  <xsl:variable name="pname">
+    <xsl:if test="namespace-uri(..)!=''">
+      <xsl:value-of select="concat('{',namespace-uri(..),'}')"/>
+    </xsl:if>
+    <xsl:value-of select="local-name(..)"/>
+  </xsl:variable>
+  <xsl:variable name="cname">
+    <xsl:if test="namespace-uri(.)!=''">
+      <xsl:value-of select="concat('{',namespace-uri(.),'}')"/>
+    </xsl:if>
+    <xsl:value-of select="local-name(.)"/>
+  </xsl:variable>
+  <xsl:call-template name="warning">
+    <xsl:with-param name="msg" select="concat($cname,' not allowed inside ',$pname,$additionalDiagnostics)"/>
+  </xsl:call-template>
+</xsl:template>
+
+<!-- figure element -->
+<xsl:template match="figure/artwork | figure/ed:replace/ed:*/artwork" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="artwork" mode="validate">
+  <xsl:call-template name="warninvalid"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- li element -->
+<xsl:template match="ol/li | ul/li" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="li" mode="validate">
+  <xsl:call-template name="warninvalid"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- list element -->
+<xsl:template match="t/list | t/ed:replace/ed:*/list" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="list" mode="validate">
+  <xsl:call-template name="warninvalid"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- t element -->
+<xsl:template match="abstract/t | abstract/ed:replace/ed:*/t |
+                     list/t | list/ed:replace/ed:*/t |
+                     note/t | note/ed:replace/ed:*/t |
+                     section/t | section/ed:replace/ed:*/t |
+                     blockquote/t |
+                     x:blockquote/t | x:blockquote/ed:replace/ed:*/t |
+                     x:note/t | x:note/ed:replace/ed:*/t |
+                     aside/t |
+                     td/t | th/t |
+                     x:lt/t | li/t | x:lt/ed:replace/ed:*/t | dd/t" mode="validate" priority="9">
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+<xsl:template match="t" mode="validate">
+  <xsl:call-template name="warninvalid"/>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<!-- xref element -->
+<xsl:template match="abstract//xref" mode="validate">
+  <xsl:call-template name="warninvalid">
+    <xsl:with-param name="additionalDiagnostics"> (inside &lt;abstract>)</xsl:with-param>
+  </xsl:call-template>
+  <xsl:apply-templates select="@*|*" mode="validate"/>
+</xsl:template>
+
+<xsl:template name="check-no-text-content">
+  <xsl:for-each select="text()">
+    <xsl:if test="normalize-space(.)!=''">
+      <xsl:call-template name="warning">
+        <xsl:with-param name="msg">No text content allowed inside &lt;<xsl:value-of select="local-name(..)"/>&gt;, but found: <xsl:value-of select="."/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="render-name">
+  <xsl:param name="n"/>
+  <xsl:param name="strip-links" select="true()"/>
+  <xsl:variable name="t">
+    <xsl:apply-templates select="$n"/>
+  </xsl:variable>
+  <xsl:choose>
+    <xsl:when test="not($strip-links)">
+      <xsl:copy-of select="exslt:node-set($t)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:apply-templates select="exslt:node-set($t)" mode="strip-links"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template name="render-name-ref">
+  <xsl:param name="n"/>
+  <xsl:variable name="t">
+    <xsl:call-template name="render-name">
+      <xsl:with-param name="n" select="$n"/>
+    </xsl:call-template>
+  </xsl:variable>
+  <xsl:apply-templates select="exslt:node-set($t)" mode="strip-ids"/>
+</xsl:template>
+
+<!-- clean up links from HTML -->
+<xsl:template match="comment()|@*" mode="strip-links"><xsl:copy/></xsl:template>
+<xsl:template match="text()" mode="strip-links"><xsl:copy/></xsl:template>
+<xsl:template match="*" mode="strip-links">
+  <xsl:element name="{local-name()}">
+  	<xsl:apply-templates select="@*|node()" mode="strip-links" />
+  </xsl:element>
+</xsl:template>
+<xsl:template match="a|xhtml:a" mode="strip-links" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	<xsl:apply-templates select="node()" mode="strip-links" />
+</xsl:template>
+<xsl:template match="node()|@*" mode="strip-ids">
+  <xsl:copy>
+	  <xsl:apply-templates select="node()|@*" mode="strip-ids" />
+  </xsl:copy>
+</xsl:template>
+<xsl:template match="@id" mode="strip-ids"/>
+
+
+<!-- customization: these templates can be overridden in an XSLT that imports from this one -->
+<xsl:template name="add-start-material"/>
+<xsl:template name="add-end-material"/>
+
+</xsl:transform>
diff --git a/test/fixtures/cache-tests/spec/lib/rfcbootstrap.xslt b/test/fixtures/cache-tests/spec/lib/rfcbootstrap.xslt
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/rfcbootstrap.xslt
@@ -0,0 +1,335 @@
+<!--
+    XSLT transformation from RFC2629 XML format to Bootstrap-ised HTML
+
+    Copyright (c) 2014-2015, Mark Nottingham (mnot@mnot.net)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Mark Nottingham nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+
+    Based upon Julian Reschke's rfc2629.xslt:
+
+    Copyright (c) 2006-2014, Julian Reschke (julian.reschke@greenbytes.de)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Julian Reschke nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="2.0"
+
+                xmlns:date="http://exslt.org/dates-and-times"
+                xmlns:ed="http://greenbytes.de/2002/rfcedit"
+                xmlns:exslt="http://exslt.org/common"
+                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
+                xmlns:myns="mailto:julian.reschke@greenbytes.de?subject=rcf2629.xslt"
+                xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+                xmlns:saxon="http://saxon.sf.net/"
+                xmlns:saxon-old="http://icl.com/saxon"
+                xmlns:x="http://purl.org/net/xml2rfc/ext"
+                xmlns:xhtml="http://www.w3.org/1999/xhtml"
+
+                exclude-result-prefixes="date ed exslt msxsl myns rdf saxon saxon-old x xhtml"
+                >
+
+  <xsl:import href="rfc2629.xslt"/>
+
+  <xsl:strip-space elements="abstract author back figure front list middle note postal reference references rfc section texttable"/>
+
+  <xsl:output method="html" encoding="utf-8" indent="no" doctype-system="about:legacy-compat" />
+
+  <!-- CSS mapping -->
+  <xsl:param name="xml2rfc-ext-css-map">cssmap.xml</xsl:param>
+
+  <!-- Library URLs -->
+  <xsl:param name="bootstrapCssUrl"
+           select="'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'" />
+  <xsl:param name="bootstrapJsUrl"
+           select="'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js'" />
+  <xsl:param name="jqueryJsUrl"
+           select="'https://code.jquery.com/jquery-3.3.1.slim.min.js'" />
+
+  <!-- navbar -->
+  <xsl:param name="navbar" select="''" />
+
+  <!-- site URLs -->
+  <xsl:param name="siteCssUrl" select="''" />
+  <xsl:param name="siteJsUrl" select="''" />
+
+  <!-- page URL -->
+  <xsl:param name="pageUrl" select="''" />
+
+  <!-- page image URL -->
+  <xsl:param name="pageImageUrl" select="''" />
+
+  <!-- site name -->
+  <xsl:param name="siteName" select="''" />
+
+  <!-- optional banner -->
+  <xsl:param name="banner" select="''" />
+
+  <!-- disable built-in ToC -->
+  <xsl:variable name="xml2rfc-toc">yes</xsl:variable>
+
+  <xsl:variable name="toc-ul-class" select="'nav-sublist list-unstyled'" />
+
+  <xsl:template name="body">
+      <body>
+        <xsl:variable name="onload">
+          <xsl:if test="$xml2rfc-ext-insert-metadata='yes' and /rfc/@number">getMeta(<xsl:value-of select="/rfc/@number"/>,"rfc.meta");</xsl:if>
+          <xsl:if test="/rfc/x:feedback">initFeedback();</xsl:if>
+          <xsl:if test="$xml2rfc-ext-refresh-from!=''">RfcRefresh.initRefresh()</xsl:if>
+        </xsl:variable>
+        <xsl:if test="$onload!=''">
+          <xsl:attribute name="onload">
+            <xsl:value-of select="$onload"/>
+          </xsl:attribute>
+        </xsl:if>
+
+        <xsl:if test="$navbar!=''">
+          <xsl:copy-of select="document($navbar)"/>
+        </xsl:if>
+
+        <!-- insert diagnostics -->
+        <xsl:call-template name="insert-diagnostics"/>
+
+        <div class="container" id="top">
+          <div class="row">
+            <div class="col-lg-4 order-last d-none d-lg-block" id="sidebar" role="navigation">
+              <div class="navbar">
+                <div class="navbar-brand">
+                  <a href="#top">
+                    <xsl:choose>
+                      <xsl:when test="/rfc/@number"><strong>RFC </strong><xsl:value-of select="/rfc/@number"/></xsl:when>
+                    </xsl:choose>
+                  </a>
+                </div>
+                <br clear="all"/>
+                <div class="">
+                    <xsl:apply-templates select="/" mode="toc">
+                      <xsl:with-param name="extra"></xsl:with-param>
+                    </xsl:apply-templates>
+                </div>
+              </div>
+            </div>
+            <div class="col-lg-8 order-first main" role="main">
+              <xsl:apply-templates select="front" />
+              <xsl:apply-templates select="middle" />
+              <xsl:call-template name="back" />
+            </div>
+          </div>
+        </div>
+        <xsl:if test="$siteJsUrl!=''">
+          <script src="{$siteJsUrl}" type="module"></script>
+        </xsl:if>
+      </body>
+  </xsl:template>
+
+
+  <xsl:template name="insertCss">
+    <meta name="viewport" content="width=device-width, initial-scale=1"/>
+    <meta property="og:type" content="article" />
+    <xsl:choose>
+      <xsl:when test="$rfcno!=''">
+        <meta property="og:title" content="{concat('RFC', $rfcno)}" />
+        <xsl:element name="meta">
+          <xsl:attribute name="property">og:description</xsl:attribute>
+          <xsl:attribute name="content">
+            <xsl:apply-templates select="front/title" mode="get-text-content" />
+          </xsl:attribute>
+        </xsl:element>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:element name="meta">
+          <xsl:attribute name="property">og:title</xsl:attribute>
+          <xsl:attribute name="content">
+            <xsl:apply-templates select="front/title" mode="get-text-content" />
+          </xsl:attribute>
+        </xsl:element>
+        <meta property="og:description" content="{normalize-space(front/abstract)}" />
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="$pageUrl!=''">
+      <meta property="og:url" content="{$pageUrl}" />
+    </xsl:if>
+    <xsl:if test="$siteName!=''">
+      <meta property="og:site_name" content="{$siteName}" />
+    </xsl:if>
+    <xsl:if test="$pageImageUrl!=''">
+      <meta property="og:image" content="{$pageImageUrl}" />
+    </xsl:if>
+    <link rel="stylesheet" type="text/css" href="{$bootstrapCssUrl}" />
+    <style type="text/css">
+      body {
+        padding-top: 80px;
+        padding-bottom: 80px;
+        position: relative;
+      }
+      .table.header th, .table.header td {
+        border-top: none;
+        padding: 0;
+      }
+      #sidebar {
+        margin-top: -10px;
+        height: 90%;
+        overflow-y: auto;
+        font-size: 90%;
+      }
+      #rfc\.meta {
+        width: 40%;
+        float: right
+      }
+      .toc ul {
+        list-style: none;
+      }
+      .filename {
+        color: rgb(119, 119, 119);
+        font-size: 23px;
+        font-weight: normal;
+        height: auto;
+        line-height: 23px;
+      }
+      dl {
+        margin-left: 1em;
+      }
+      dl.dl-horizontal: {
+        margin-left: 0;
+      }
+      dl > dt {
+        float: left;
+        margin-right: 1em;
+      }
+      dl.nohang > dt {
+        float: none;
+      }
+      dl > dd {
+        margin-bottom: .5em;
+      }
+      dl.compact > dd {
+        margin-bottom: 0em;
+      }
+      dl > dd > dl {
+        margin-top: 0.5em;
+        margin-bottom: 0em;
+      }
+      ul.empty {<!-- spacing between two entries in definition lists -->
+        list-style-type: none;
+      }
+      ul.empty li {
+        margin-top: .5em;
+      }
+      td.reference {
+        padding-right: 1em;
+        vertical-align: top;
+      }
+      .feedback {
+        position: fixed;
+        bottom: 5px;
+        right: 5px;
+      }
+      .fbbutton {
+        margin-left: 5px;
+      }
+      h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+        color: rgb(51, 51, 51);
+      }
+      span.tt {
+        font: 11pt consolas, monospace;
+        font-size-adjust: none;
+      }
+      div.banner {
+        background-color: #fee;
+        border: 2px solid #633;
+        padding: 8px 12px;
+        margin-bottom: 10px;
+      }
+      div.banner p {
+        font-size: 1.2em;
+      }
+    </style>
+    <xsl:if test="$siteCssUrl!=''">
+      <link rel="stylesheet" type="text/css" href="{$siteCssUrl}" />
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="abstract">
+    <xsl:call-template name="check-no-text-content"/>
+    <hr/>
+    <h2 id="{$anchor-pref}abstract"><a href="#{$anchor-pref}abstract">Abstract</a></h2>
+    <div class="lead">
+      <xsl:apply-templates />
+    </div>
+    <xsl:if test="$banner!=''">
+      <div class="banner">
+        <p><xsl:value-of select="$banner"/></p>
+      </div>
+    </xsl:if>
+
+  </xsl:template>
+
+  <xsl:template match="/" mode="toc">
+    <xsl:param name="extra">d-lg-none</xsl:param>
+    <div class="toc {$extra}">
+      <ul>
+        <xsl:apply-templates mode="toc" />
+      </ul>
+    </div>
+  </xsl:template>
+
+  <xsl:template name="insertTocAppendix">
+  </xsl:template>
+
+  <xsl:template name="get-generator">
+    <xsl:variable name="gen">
+      <xsl:text>https://github.com/mnot/RFCBootstrap </xsl:text>
+      <xsl:value-of select="concat('XSLT vendor: ',system-property('xsl:vendor'),' ',system-property('xsl:vendor-url'))" />
+    </xsl:variable>
+    <xsl:value-of select="$gen" />
+  </xsl:template>
+
+
+</xsl:transform>
diff --git a/test/fixtures/cache-tests/spec/lib/xreffer.xslt b/test/fixtures/cache-tests/spec/lib/xreffer.xslt
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/lib/xreffer.xslt
@@ -0,0 +1,32 @@
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               xmlns:x="http://purl.org/net/xml2rfc/ext"
+               version="1.0" 
+>
+
+<xsl:output encoding="UTF-8" />
+
+<!-- rules for identity transformations -->
+
+<xsl:template match="node()|@*"><xsl:copy><xsl:apply-templates select="node()|@*" /></xsl:copy></xsl:template>
+
+<xsl:template match="/">
+	<xsl:copy><xsl:apply-templates select="node()" /></xsl:copy>
+</xsl:template>
+
+<xsl:template match="xref">
+  <xsl:variable name="t" select="@target"/>
+  <xsl:variable name="n" select="//*[@anchor=$t]"/>
+  <xsl:choose>
+    <xsl:when test="$n/self::x:has and $n/@target">
+      <xref target="{$n/../../@anchor}" x:rel="#{$n/@target}"/>
+    </xsl:when>
+    <xsl:when test="$n/self::x:has">
+      <xref target="{$n/../../@anchor}" x:rel="#{$t}"/>
+    </xsl:when>
+    <xsl:otherwise>
+    	<xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+</xsl:transform>
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/rfc9110.xml b/test/fixtures/cache-tests/spec/rfc9110.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/rfc9110.xml
@@ -0,0 +1,13578 @@
+<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type='text/xsl' href='lib/myxml2rfc.xslt'?>
+<?rfc compact="yes"?>
+<?rfc subcompact="no" ?>
+<?rfc linkmailto="no" ?>
+<?rfc editing="no" ?>
+<?rfc comments="yes"?>
+<?rfc inline="yes"?>
+<?rfc rfcedstyle="yes"?>
+<?rfc-ext allow-markup-in-artwork="yes" ?>
+<?rfc-ext map-dfn="none" ?>
+<?rfc-ext html-pretty-print="prettyprint https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"?>
+<?rfc-ext include-references-in-index="yes" ?>
+<?rfc-ext xml2rfc-backend="202007"?>
+<rfc xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:x="http://purl.org/net/xml2rfc/ext" obsoletes="2818, 7230, 7231, 7232, 7233, 7235, 7538, 7615, 7694" updates="3864" category="std" x:maturity-level="internet" ipr="pre5378Trust200902" number="9110" seriesNo="97" version="3" tocDepth="4" sortRefs="true">
+<front>
+
+  <title>HTTP Semantics</title>
+
+  <author fullname="Roy T. Fielding" initials="R." surname="Fielding" role="editor">
+    <organization>Adobe</organization>
+    <address>
+      <postal>
+        <postalLine>345 Park Ave</postalLine>
+        <postalLine>San Jose, CA 95110</postalLine>
+        <country>United States of America</country>
+      </postal>
+      <email>fielding@gbiv.com</email>
+      <uri>https://roy.gbiv.com/</uri>
+    </address>
+  </author>
+
+  <author fullname="Mark Nottingham" initials="M." surname="Nottingham" role="editor">
+    <organization>Fastly</organization>
+    <address>
+       <postal>
+         <postalLine>Prahran</postalLine>
+         <country>Australia</country>
+       </postal>
+      <email>mnot@mnot.net</email>
+      <uri>https://www.mnot.net/</uri>
+    </address>
+  </author>
+
+  <author fullname="Julian Reschke" initials="J." surname="Reschke" role="editor">
+    <organization abbrev="greenbytes">greenbytes GmbH</organization>
+    <address>
+      <postal>
+        <postalLine>Hafenweg 16</postalLine>
+        <postalLine>48155 Münster</postalLine>
+        <country>Germany</country>
+      </postal>
+      <email>julian.reschke@greenbytes.de</email>
+      <uri>https://greenbytes.de/tech/webdav/</uri>
+    </address>
+  </author>
+
+  <date year="2022" month="June"/>
+
+  <area>Applications and Real-Time</area>
+  <workgroup>HTTP Working Group</workgroup>
+
+  <keyword>Hypertext Transfer Protocol</keyword>
+  <keyword>HTTP</keyword>
+  <keyword>HTTP semantics</keyword>
+  <keyword>HTTP content</keyword>
+  <keyword>HTTP method</keyword>
+  <keyword>HTTP status code</keyword>
+
+<abstract>
+<t>
+   The Hypertext Transfer Protocol (HTTP) is a stateless application-level
+   protocol for distributed, collaborative, hypertext information systems.
+   This document describes the overall architecture of HTTP, establishes common
+   terminology, and defines aspects of the protocol that are shared by all
+   versions. In this definition are core protocol elements, extensibility
+   mechanisms, and the "http" and "https" Uniform Resource Identifier (URI)
+   schemes.
+</t>
+<t>
+   This document updates RFC 3864 and
+   obsoletes RFCs 2818, 7231, 7232, 7233,
+   7235, 7538, 7615, 7694, and portions of 7230.
+</t>
+</abstract>
+</front>
+
+<middle>
+<section title="Introduction" anchor="introduction">
+<section title="Purpose" anchor="purpose">
+<t>
+   The Hypertext Transfer Protocol (HTTP) is a family of stateless,
+   application-level, request/response protocols that share a generic interface,
+   extensible semantics, and self-descriptive messages to enable flexible
+   interaction with network-based hypertext information systems.
+</t>
+<t>
+   HTTP hides the details of how a service is implemented by presenting a
+   uniform interface to clients that is independent of the types of resources
+   provided. Likewise, servers do not need to be aware of each client's
+   purpose: a request can be considered in isolation rather than being
+   associated with a specific type of client or a predetermined sequence of
+   application steps. This allows general-purpose implementations to be used
+   effectively in many different contexts, reduces interaction complexity, and
+   enables independent evolution over time.
+</t>
+<t>
+   HTTP is also designed for use as an intermediation protocol, wherein
+   proxies and gateways can translate non-HTTP information systems into a
+   more generic interface.
+</t>
+<t>
+   One consequence of this flexibility is that the protocol cannot be
+   defined in terms of what occurs behind the interface. Instead, we
+   are limited to defining the syntax of communication, the intent
+   of received communication, and the expected behavior of recipients.
+   If the communication is considered in isolation, then successful
+   actions ought to be reflected in corresponding changes to the
+   observable interface provided by servers. However, since multiple
+   clients might act in parallel and perhaps at cross-purposes, we
+   cannot require that such changes be observable beyond the scope
+   of a single response.
+</t>
+</section>
+
+<section title="History and Evolution" anchor="history.and.evolution">
+<t>
+   HTTP has been the primary information transfer protocol for the World Wide
+   Web since its introduction in 1990. It began as a trivial mechanism for
+   low-latency requests, with a single method (GET) to request transfer of a
+   presumed hypertext document identified by a given pathname.
+   As the Web grew, HTTP was extended to enclose requests and responses within
+   messages, transfer arbitrary data formats using MIME-like media types, and
+   route requests through intermediaries. These protocols were eventually
+   defined as HTTP/0.9 and HTTP/1.0 (see <xref target="HTTP10"/>).
+</t>
+<t>
+   HTTP/1.1 was designed to refine the protocol's features while retaining
+   compatibility with the existing text-based messaging syntax, improving
+   its interoperability, scalability, and robustness across the Internet.
+   This included length-based data delimiters for both fixed and dynamic
+   (chunked) content, a consistent framework for content negotiation,
+   opaque validators for conditional requests, cache controls for better
+   cache consistency, range requests for partial updates, and default
+   persistent connections. HTTP/1.1 was introduced in 1995 and published on
+   the Standards Track in 1997 <xref target="RFC2068"/>, revised in
+   1999 <xref target="RFC2616"/>, and revised again in 2014
+   (<xref target="RFC7230"/> through <xref target="RFC7235"/>).
+</t>
+<t>
+   HTTP/2 (<xref target="HTTP2"/>) introduced a multiplexed session layer
+   on top of the existing TLS and TCP protocols for exchanging concurrent
+   HTTP messages with efficient field compression and server push.
+   HTTP/3 (<xref target="HTTP3"/>) provides greater independence for concurrent
+   messages by using QUIC as a secure multiplexed transport over UDP instead of
+   TCP.
+</t>
+<t>
+   All three major versions of HTTP rely on the semantics defined by
+   this document. They have not obsoleted each other because each one has
+   specific benefits and limitations depending on the context of use.
+   Implementations are expected to choose the most appropriate transport and
+   messaging syntax for their particular context.
+</t>
+<t>
+   This revision of HTTP separates the definition of semantics (this document)
+   and caching (<xref target="CACHING"/>) from the current HTTP/1.1 messaging
+   syntax (<xref target="HTTP11"/>) to allow each major protocol version
+   to progress independently while referring to the same core semantics.
+</t>
+</section>
+
+<section title="Core Semantics" anchor="core.semantics">
+<t>
+   HTTP provides a uniform interface for interacting with a resource
+   (<xref target="resources"/>) — regardless of its type, nature, or
+   implementation — by sending messages that manipulate or transfer
+   representations (<xref target="representations"/>).
+</t>
+<t>
+   Each message is either a request or a response. A client constructs request
+   messages that communicate its intentions and routes those messages toward
+   an identified origin server. A server listens for requests, parses each
+   message received, interprets the message semantics in relation to the
+   identified target resource, and responds to that request with one or more
+   response messages. The client examines received responses to see if its
+   intentions were carried out, determining what to do next based on the
+   status codes and content received.
+</t>
+<t>
+   HTTP semantics include the intentions defined by each request method
+   (<xref target="methods"/>), extensions to those semantics that might be
+   described in request header fields,
+   status codes that describe the response (<xref target="status.codes"/>), and
+   other control data and resource metadata that might be given in response
+   fields.
+</t>
+<t><iref item="content negotiation"/>
+   Semantics also include representation metadata that describe how
+   content is intended to be interpreted by a recipient, request header
+   fields that might influence content selection, and the various selection
+   algorithms that are collectively referred to as
+   <x:dfn>content negotiation</x:dfn> (<xref target="content.negotiation"/>).
+</t>
+</section>
+
+<section title="Specifications Obsoleted by This Document" anchor="specifications.obsoleted.by.this.document">
+<table align="left">
+  <thead>
+    <tr>
+      <th>Title</th>
+      <th>Reference</th>
+      <th>See</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>HTTP Over TLS</td>
+      <td><xref target="RFC2818"/></td>
+      <td><xref target="changes.from.rfc.2818" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP/1.1 Message Syntax and Routing [*]</td>
+      <td><xref target="RFC7230"/></td>
+      <td><xref target="changes.from.rfc.7230" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP/1.1 Semantics and Content</td>
+      <td><xref target="RFC7231"/></td>
+      <td><xref target="changes.from.rfc.7231" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP/1.1 Conditional Requests</td>
+      <td><xref target="RFC7232"/></td>
+      <td><xref target="changes.from.rfc.7232" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP/1.1 Range Requests</td>
+      <td><xref target="RFC7233"/></td>
+      <td><xref target="changes.from.rfc.7233" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP/1.1 Authentication</td>
+      <td><xref target="RFC7235"/></td>
+      <td><xref target="changes.from.rfc.7235" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP Status Code 308 (Permanent Redirect)</td>
+      <td><xref target="RFC7538"/></td>
+      <td><xref target="changes.from.rfc.7538" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP Authentication-Info and Proxy-Authentication-Info
+          Response Header Fields</td>
+      <td><xref target="RFC7615"/></td>
+      <td><xref target="changes.from.rfc.7615" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HTTP Client-Initiated Content-Encoding</td>
+      <td><xref target="RFC7694"/></td>
+      <td><xref target="changes.from.rfc.7694" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+<t>
+   [*] This document only obsoletes the portions of
+   <xref target="RFC7230" format="none">RFC 7230</xref> that are independent of
+   the HTTP/1.1 messaging syntax and connection management; the remaining
+   bits of <xref target="RFC7230" format="none">RFC 7230</xref> are
+   obsoleted by "HTTP/1.1" <xref target="HTTP11"/>.
+</t>
+</section>
+</section>
+
+<section title="Conformance" anchor="conformance">
+
+<section title="Syntax Notation" anchor="notation">
+<iref primary="true" item="Grammar" subitem="ALPHA"/>
+<iref primary="true" item="Grammar" subitem="CR"/>
+<iref primary="true" item="Grammar" subitem="CRLF"/>
+<iref primary="true" item="Grammar" subitem="CTL"/>
+<iref primary="true" item="Grammar" subitem="DIGIT"/>
+<iref primary="true" item="Grammar" subitem="DQUOTE"/>
+<iref primary="true" item="Grammar" subitem="HEXDIG"/>
+<iref primary="true" item="Grammar" subitem="HTAB"/>
+<iref primary="true" item="Grammar" subitem="LF"/>
+<iref primary="true" item="Grammar" subitem="OCTET"/>
+<iref primary="true" item="Grammar" subitem="SP"/>
+<iref primary="true" item="Grammar" subitem="VCHAR"/>
+<t>
+   This specification uses the Augmented Backus-Naur Form (ABNF) notation of
+   <xref target="RFC5234"/>, extended with the notation for case-sensitivity
+   in strings defined in <xref target="RFC7405"/>.
+</t>
+<t>
+   It also uses a list extension, defined in <xref target="abnf.extension"/>,
+   that allows for compact definition of comma-separated lists using a "#"
+   operator (similar to how the "*" operator indicates repetition). <xref target="collected.abnf"/> shows the collected grammar with all list
+   operators expanded to standard ABNF notation.
+</t>
+<t>
+   As a convention, ABNF rule names prefixed with "obs-" denote
+   obsolete grammar rules that appear for historical reasons.
+</t>
+<t anchor="core.rules">
+  <x:anchor-alias value="ALPHA"/>
+  <x:anchor-alias value="CR"/>
+  <x:anchor-alias value="CRLF"/>
+  <x:anchor-alias value="CTL"/>
+  <x:anchor-alias value="DIGIT"/>
+  <x:anchor-alias value="DQUOTE"/>
+  <x:anchor-alias value="HEXDIG"/>
+  <x:anchor-alias value="HTAB"/>
+  <x:anchor-alias value="LF"/>
+  <x:anchor-alias value="OCTET"/>
+  <x:anchor-alias value="SP"/>
+  <x:anchor-alias value="VCHAR"/>
+  The following core rules are included by
+  reference, as defined in <xref target="RFC5234" x:fmt="of" x:sec="B.1"/>:
+  ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls),
+  DIGIT (decimal 0-9), DQUOTE (double quote),
+  HEXDIG (hexadecimal 0-9/A-F/a-f), HTAB (horizontal tab), LF (line feed),
+  OCTET (any 8-bit sequence of data), SP (space), and
+  VCHAR (any visible US-ASCII character).
+</t>
+<t>
+   <xref target="fields.components"/> defines some generic syntactic
+   components for field values.
+</t>
+<t>
+   This specification uses the terms
+   "character",
+   "character encoding scheme",
+   "charset", and
+   "protocol element"
+   as they are defined in <xref target="RFC6365"/>.
+</t>
+</section>
+
+<section title="Requirements Notation" anchor="requirements.notation">
+<t>
+   The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL NOT</bcp14>",
+   "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
+   "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
+   described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
+   appear in all capitals, as shown here.
+</t>
+<t>
+   This specification targets conformance criteria according to the role of
+   a participant in HTTP communication.  Hence, requirements are placed
+   on senders, recipients, clients, servers, user agents, intermediaries,
+   origin servers, proxies, gateways, or caches, depending on what behavior
+   is being constrained by the requirement. Additional requirements
+   are placed on implementations, resource owners, and protocol element
+   registrations when they apply beyond the scope of a single communication.
+</t>
+<t>
+   The verb "generate" is used instead of "send" where a requirement applies
+   only to implementations that create the protocol element, rather than an
+   implementation that forwards a received element downstream.
+</t>
+<t>
+   An implementation is considered conformant if it complies with all of the
+   requirements associated with the roles it partakes in HTTP.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate protocol elements that do not match the grammar
+   defined by the corresponding ABNF rules.
+   Within a given message, a sender <bcp14>MUST NOT</bcp14> generate protocol elements or
+   syntax alternatives that are only allowed to be generated by participants in
+   other roles (i.e., a role that the sender does not have for that message).
+</t>
+<t>
+   Conformance to HTTP includes both conformance to the particular messaging
+   syntax of the protocol version in use and conformance to the semantics of
+   protocol elements sent. For example, a client that claims conformance to
+   HTTP/1.1 but fails to recognize the features required of HTTP/1.1
+   recipients will fail to interoperate with servers that adjust their
+   responses in accordance with those claims.
+   Features that reflect user choices, such as content negotiation and
+   user-selected extensions, can impact application behavior beyond the
+   protocol stream; sending protocol elements that inaccurately reflect a
+   user's choices will confuse the user and inhibit choice.
+</t>
+<t>
+   When an implementation fails semantic conformance, recipients of that
+   implementation's messages will eventually develop workarounds to adjust
+   their behavior accordingly. A recipient <bcp14>MAY</bcp14> employ such workarounds while
+   remaining conformant to this protocol if the workarounds are limited to the
+   implementations at fault. For example, servers often scan portions of the
+   User-Agent field value, and user agents often scan the Server field value,
+   to adjust their own behavior with respect to known bugs or poorly chosen
+   defaults.
+</t>
+</section>
+
+<section title="Length Requirements" anchor="length.requirements">
+<t>
+   A recipient <bcp14>SHOULD</bcp14> parse a received protocol element defensively, with
+   only marginal expectations that the element will conform to its ABNF
+   grammar and fit within a reasonable buffer size.
+</t>
+<t>
+   HTTP does not have specific length limitations for many of its protocol
+   elements because the lengths that might be appropriate will vary widely,
+   depending on the deployment context and purpose of the implementation.
+   Hence, interoperability between senders and recipients depends on shared
+   expectations regarding what is a reasonable length for each protocol
+   element. Furthermore, what is commonly understood to be a reasonable length
+   for some protocol elements has changed over the course of the past three
+   decades of HTTP use and is expected to continue changing in the future.
+</t>
+<t>
+   At a minimum, a recipient <bcp14>MUST</bcp14> be able to parse and process protocol
+   element lengths that are at least as long as the values that it generates
+   for those same protocol elements in other messages. For example, an origin
+   server that publishes very long URI references to its own resources needs
+   to be able to parse and process those same references when received as a
+   target URI.
+</t>
+<t>
+   Many received protocol elements are only parsed to the extent necessary to
+   identify and forward that element downstream. For example, an intermediary
+   might parse a received field into its field name and field value components,
+   but then forward the field without further parsing inside the field value.
+</t>
+</section>
+
+<section title="Error Handling" anchor="error.handling">
+<t>
+   A recipient <bcp14>MUST</bcp14> interpret a received protocol element according to the
+   semantics defined for it by this specification, including extensions to
+   this specification, unless the recipient has determined (through experience
+   or configuration) that the sender incorrectly implements what is implied by
+   those semantics.
+   For example, an origin server might disregard the contents of a received
+   <x:ref>Accept-Encoding</x:ref> header field if inspection of the
+   <x:ref>User-Agent</x:ref> header field indicates a specific implementation
+   version that is known to fail on receipt of certain content codings.
+</t>
+<t>
+   Unless noted otherwise, a recipient <bcp14>MAY</bcp14> attempt to recover a usable
+   protocol element from an invalid construct.  HTTP does not define
+   specific error handling mechanisms except when they have a direct impact
+   on security, since different applications of the protocol require
+   different error handling strategies.  For example, a Web browser might
+   wish to transparently recover from a response where the
+   <x:ref>Location</x:ref> header field doesn't parse according to the ABNF,
+   whereas a systems control client might consider any form of error recovery
+   to be dangerous.
+</t>
+<t>
+   Some requests can be automatically retried by a client in the event of
+   an underlying connection failure, as described in
+   <xref target="idempotent.methods"/>.
+</t>
+</section>
+
+<section title="Protocol Version" anchor="protocol.version">
+<t>
+   HTTP's version number consists of two decimal digits separated by a "."
+   (period or decimal point). The first digit (major version) indicates the
+   messaging syntax, whereas the second digit (minor version)
+   indicates the highest minor version within that major version to which the
+   sender is conformant (able to understand for future communication).
+</t>
+<t>
+   While HTTP's core semantics don't change between protocol versions, their
+   expression "on the wire" can change, and so the
+   HTTP version number changes when incompatible changes are made to the wire
+   format. Additionally, HTTP allows incremental, backwards-compatible
+   changes to be made to the protocol without changing its version through
+   the use of defined extension points (<xref target="extending"/>).
+</t>
+<t>
+   The protocol version as a whole indicates the sender's conformance with
+   the set of requirements laid out in that version's corresponding
+   specification(s).
+   For example, the version "HTTP/1.1" is defined by the combined
+   specifications of this document, "HTTP Caching" <xref target="CACHING"/>,
+   and "HTTP/1.1" <xref target="HTTP11"/>.
+</t>
+<t>
+   HTTP's major version number is incremented when an incompatible message
+   syntax is introduced. The minor number is incremented when changes made to
+   the protocol have the effect of adding to the message semantics or
+   implying additional capabilities of the sender.
+</t>
+<t>
+   The minor version advertises the sender's communication capabilities even
+   when the sender is only using a backwards-compatible subset of the
+   protocol, thereby letting the recipient know that more advanced features
+   can be used in response (by servers) or in future requests (by clients).
+</t>
+<t>
+   When a major version of HTTP does not define any minor versions, the minor
+   version "0" is implied. The "0" is used when referring to that protocol
+   within elements that require a minor version identifier.
+</t>
+</section>
+</section>
+
+<section title="Terminology and Core Concepts" anchor="terminology">
+<t>
+   HTTP was created for the World Wide Web (WWW) architecture
+   and has evolved over time to support the scalability needs of a worldwide
+   hypertext system. Much of that architecture is reflected in the terminology
+   used to define HTTP.
+</t>
+
+<section title="Resources" anchor="resources">
+   <iref primary="true" item="resource"/>
+   <x:anchor-alias value="resource"/>
+<t>
+   The target of an HTTP request is called a <x:dfn>resource</x:dfn>.
+   HTTP does not limit the nature of a resource; it merely
+   defines an interface that might be used to interact with resources.
+   Most resources are identified by a Uniform Resource Identifier (URI), as
+   described in <xref target="uri"/>.
+</t>
+<t>
+   One design goal of HTTP is to separate resource identification from
+   request semantics, which is made possible by vesting the request
+   semantics in the request method (<xref target="methods"/>) and a few
+   request-modifying header fields.
+   A resource cannot treat a request in a manner inconsistent with the
+   semantics of the method of the request. For example, though the URI of a
+   resource might imply semantics that are not safe, a client can expect the
+   resource to avoid actions that are unsafe when processing a request with a
+   safe method (see <xref target="safe.methods"/>).
+</t>
+<t>
+   HTTP relies upon the Uniform Resource Identifier (URI)
+   standard <xref target="URI"/> to indicate the target resource
+   (<xref target="target.resource"/>) and relationships between resources.
+</t>
+</section>
+
+<section title="Representations" anchor="representations">
+<iref primary="true" item="representation"/>
+<x:anchor-alias value="representation"/>
+<t>
+   A <x:dfn>representation</x:dfn> is information
+   that is intended to reflect a past, current, or desired state of a given
+   resource, in a format that can be readily communicated via the protocol.
+   A representation consists of a set of representation metadata and a
+   potentially unbounded stream of representation data
+   (<xref target="representation.data.and.metadata"/>).
+</t>
+<t>
+   HTTP allows "information hiding" behind its uniform interface by defining
+   communication with respect to a transferable representation of the resource
+   state, rather than transferring the resource itself. This allows the
+   resource identified by a URI to be anything, including temporal functions
+   like "the current weather in Laguna Beach", while potentially providing
+   information that represents that resource at the time a message is
+   generated <xref target="REST"/>.
+</t>
+<t>
+   The uniform interface is similar to a window through which one can observe
+   and act upon a thing only through the communication of messages to an
+   independent actor on the other side. A shared abstraction is needed to
+   represent ("take the place of") the current or desired state of that thing
+   in our communications. When a representation is hypertext, it can provide
+   both a representation of the resource state and processing instructions
+   that help guide the recipient's future interactions.
+</t>
+<t anchor="selected.representation">
+   <x:anchor-alias value="selected representation"/>
+   <iref primary="true" item="selected representation"/>
+   A <x:ref>target resource</x:ref> might be provided with, or be capable of
+   generating, multiple representations that are each intended to reflect the
+   resource's current state. An algorithm, usually based on
+   <x:ref>content negotiation</x:ref> (<xref target="content.negotiation"/>),
+   would be used to select one of those representations as being most
+   applicable to a given request.
+   This <x:dfn>selected representation</x:dfn> provides the data and metadata
+   for evaluating conditional requests (<xref target="conditional.requests"/>)
+   and constructing the content for <x:ref>200 (OK)</x:ref>,
+   <x:ref>206 (Partial Content)</x:ref>, and
+   <x:ref>304 (Not Modified)</x:ref> responses to GET (<xref target="GET"/>).
+</t>
+</section>
+
+<section title="Connections, Clients, and Servers" anchor="connections">
+<iref primary="true" item="client"/>
+<iref primary="true" item="server"/>
+<iref primary="true" item="connection"/>
+<x:anchor-alias value="connection"/>
+<t>
+   HTTP is a client/server protocol that operates over a reliable
+   transport- or session-layer <x:dfn>connection</x:dfn>.
+</t>
+<t>
+   An HTTP <x:dfn>client</x:dfn> is a program that establishes a connection
+   to a server for the purpose of sending one or more HTTP requests.
+   An HTTP <x:dfn>server</x:dfn> is a program that accepts connections
+   in order to service HTTP requests by sending HTTP responses.
+</t>
+<t>
+   The terms client and server refer only to the roles that
+   these programs perform for a particular connection.  The same program
+   might act as a client on some connections and a server on others.
+</t>
+<t>
+   HTTP is defined as a stateless protocol, meaning that each request message's semantics
+   can be understood in isolation, and that the relationship between connections
+   and messages on them has no impact on the interpretation of those messages.
+   For example, a CONNECT request (<xref target="CONNECT"/>) or a request with
+   the Upgrade header field (<xref target="field.upgrade"/>) can occur at any time,
+   not just in the first message on a connection. Many implementations depend on
+   HTTP's stateless design in order to reuse proxied connections or dynamically
+   load balance requests across multiple servers.
+</t>
+<t>
+   As a result, a server <bcp14>MUST NOT</bcp14>
+   assume that two requests on the same connection are from the same user
+   agent unless the connection is secured and specific to that agent.
+   Some non-standard HTTP extensions (e.g., <xref target="RFC4559"/>) have
+   been known to violate this requirement, resulting in security and
+   interoperability problems.
+</t>
+</section>
+
+<section title="Messages" anchor="messages">
+<iref primary="true" item="messages"/>
+<iref item="message"/>
+<iref primary="true" item="sender"/>
+<iref primary="true" item="recipient"/>
+<iref primary="true" item="request"/>
+<iref primary="true" item="response"/>
+<t>
+   HTTP is a stateless request/response protocol for exchanging
+   <x:dfn>messages</x:dfn> across a <x:ref>connection</x:ref>.
+   The terms <x:dfn>sender</x:dfn> and <x:dfn>recipient</x:dfn> refer to
+   any implementation that sends or receives a given message, respectively.
+</t>
+<t>
+   A client sends requests to a server in the form of a <x:dfn>request</x:dfn>
+   message with a method (<xref target="methods"/>) and request target
+   (<xref target="target.resource"/>). The request might also contain
+   header fields (<xref target="header.fields"/>) for request modifiers,
+   client information, and representation metadata,
+   content (<xref target="content"/>) intended for processing
+   in accordance with the method, and
+   trailer fields (<xref target="trailer.fields"/>) to communicate information
+   collected while sending the content.
+</t>
+<t>
+   A server responds to a client's request by sending one or more
+   <x:dfn>response</x:dfn> messages, each including a status
+   code (<xref target="status.codes"/>). The response might also contain
+   header fields for server information, resource metadata, and representation
+   metadata, content to be interpreted in accordance with the status
+   code, and trailer fields to communicate information
+   collected while sending the content.
+</t>
+</section>
+
+<section title="User Agents" anchor="user.agent">
+<iref primary="true" item="user agent"/>
+<iref primary="true" item="browser"/>
+<iref primary="true" item="spider"/>
+<t>
+   The term <x:dfn>user agent</x:dfn> refers to any of the various
+   client programs that initiate a request.
+</t>
+<t>
+   The most familiar form of user agent is the general-purpose Web browser, but
+   that's only a small percentage of implementations. Other common user agents
+   include spiders (web-traversing robots), command-line tools, billboard
+   screens, household appliances, scales, light bulbs, firmware update scripts,
+   mobile apps, and communication devices in a multitude of shapes and sizes.
+</t>
+<t>
+   Being a user agent does not imply that there is a human user directly
+   interacting with the software agent at the time of a request. In many
+   cases, a user agent is installed or configured to run in the background
+   and save its results for later inspection (or save only a subset of those
+   results that might be interesting or erroneous). Spiders, for example, are
+   typically given a start URI and configured to follow certain behavior while
+   crawling the Web as a hypertext graph.
+</t>
+<t>
+   Many user agents cannot, or choose not to,
+   make interactive suggestions to their user or provide adequate warning for
+   security or privacy concerns. In the few cases where this
+   specification requires reporting of errors to the user, it is acceptable
+   for such reporting to only be observable in an error console or log file.
+   Likewise, requirements that an automated action be confirmed by the user
+   before proceeding might be met via advance configuration choices,
+   run-time options, or simple avoidance of the unsafe action; confirmation
+   does not imply any specific user interface or interruption of normal
+   processing if the user has already made that choice.
+</t>
+</section>
+
+<section title="Origin Server" anchor="origin.server">
+<iref primary="true" item="origin server"/>
+<t>
+   The term <x:dfn>origin server</x:dfn> refers to a program that can
+   originate authoritative responses for a given target resource.
+</t>
+<t>
+   The most familiar form of origin server are large public websites.
+   However, like user agents being equated with browsers, it is easy to be
+   misled into thinking that all origin servers are alike.
+   Common origin servers also include home automation units, configurable
+   networking components, office machines, autonomous robots, news feeds,
+   traffic cameras, real-time ad selectors, and video-on-demand platforms.
+</t>
+<t>
+   Most HTTP communication consists of a retrieval request (GET) for
+   a representation of some resource identified by a URI.  In the
+   simplest case, this might be accomplished via a single bidirectional
+   connection (===) between the user agent (UA) and the origin server (O).
+</t>
+<figure><artwork type="drawing">
+         request   &gt;
+    <x:highlight>UA</x:highlight> ======================================= <x:highlight>O</x:highlight>
+                                &lt;   response
+</artwork></figure>
+</section>
+
+<section title="Intermediaries" anchor="intermediaries">
+<iref primary="true" item="intermediary"/>
+<t>
+   HTTP enables the use of intermediaries to satisfy requests through
+   a chain of connections.  There are three common forms of HTTP
+   <x:dfn>intermediary</x:dfn>: proxy, gateway, and tunnel.  In some cases,
+   a single intermediary might act as an origin server, proxy, gateway,
+   or tunnel, switching behavior based on the nature of each request.
+</t>
+<figure><artwork type="drawing">
+         &gt;             &gt;             &gt;             &gt;
+    <x:highlight>UA</x:highlight> =========== <x:highlight>A</x:highlight> =========== <x:highlight>B</x:highlight> =========== <x:highlight>C</x:highlight> =========== <x:highlight>O</x:highlight>
+               &lt;             &lt;             &lt;             &lt;
+</artwork></figure>
+<t>
+   The figure above shows three intermediaries (A, B, and C) between the
+   user agent and origin server. A request or response message that
+   travels the whole chain will pass through four separate connections.
+   Some HTTP communication options
+   might apply only to the connection with the nearest, non-tunnel
+   neighbor, only to the endpoints of the chain, or to all connections
+   along the chain. Although the diagram is linear, each participant might
+   be engaged in multiple, simultaneous communications. For example, B
+   might be receiving requests from many clients other than A, and/or
+   forwarding requests to servers other than C, at the same time that it
+   is handling A's request. Likewise, later requests might be sent through a
+   different path of connections, often based on dynamic configuration for
+   load balancing.
+</t>
+<t>
+<iref primary="true" item="upstream"/><iref primary="true" item="downstream"/>
+<iref primary="true" item="inbound"/><iref primary="true" item="outbound"/>
+   The terms <x:dfn>upstream</x:dfn> and <x:dfn>downstream</x:dfn> are
+   used to describe directional requirements in relation to the message flow:
+   all messages flow from upstream to downstream.
+   The terms <x:dfn>inbound</x:dfn> and <x:dfn>outbound</x:dfn> are used to
+   describe directional requirements in relation to the request route:
+   inbound means "toward the origin server", whereas
+   outbound means "toward the user agent".
+</t>
+<t><iref primary="true" item="proxy"/>
+   A <x:dfn>proxy</x:dfn> is a message-forwarding agent that is chosen by the
+   client, usually via local configuration rules, to receive requests
+   for some type(s) of absolute URI and attempt to satisfy those
+   requests via translation through the HTTP interface.  Some translations
+   are minimal, such as for proxy requests for "http" URIs, whereas
+   other requests might require translation to and from entirely different
+   application-level protocols. Proxies are often used to group an
+   organization's HTTP requests through a common intermediary for the
+   sake of security services, annotation services, or shared caching. Some
+   proxies are designed to apply transformations to selected messages or
+   content while they are being forwarded, as described in
+   <xref target="message.transformations"/>.
+</t>
+<t><iref primary="true" item="gateway"/><iref primary="true" item="reverse proxy"/>
+<iref primary="true" item="accelerator"/>
+   A <x:dfn>gateway</x:dfn> (a.k.a. <x:dfn>reverse proxy</x:dfn>) is an
+   intermediary that acts as an origin server for the outbound connection but
+   translates received requests and forwards them inbound to another server or
+   servers. Gateways are often used to encapsulate legacy or untrusted
+   information services, to improve server performance through
+   <x:dfn>accelerator</x:dfn> caching, and to enable partitioning or load
+   balancing of HTTP services across multiple machines.
+</t>
+<t>
+   All HTTP requirements applicable to an origin server
+   also apply to the outbound communication of a gateway.
+   A gateway communicates with inbound servers using any protocol that
+   it desires, including private extensions to HTTP that are outside
+   the scope of this specification.  However, an HTTP-to-HTTP gateway
+   that wishes to interoperate with third-party HTTP servers needs to conform
+   to user agent requirements on the gateway's inbound connection.
+</t>
+<t><iref primary="true" item="tunnel"/>
+   A <x:dfn>tunnel</x:dfn> acts as a blind relay between two connections
+   without changing the messages. Once active, a tunnel is not
+   considered a party to the HTTP communication, though the tunnel might
+   have been initiated by an HTTP request. A tunnel ceases to exist when
+   both ends of the relayed connection are closed. Tunnels are used to
+   extend a virtual connection through an intermediary, such as when
+   Transport Layer Security (TLS, <xref target="TLS13"/>) is used to
+   establish confidential communication through a shared firewall proxy.
+</t>
+<t>
+   The above categories for intermediary only consider those acting as
+   participants in the HTTP communication.  There are also intermediaries
+   that can act on lower layers of the network protocol stack, filtering or
+   redirecting HTTP traffic without the knowledge or permission of message
+   senders. Network intermediaries are indistinguishable (at a protocol level)
+   from an on-path attacker, often introducing security flaws or
+   interoperability problems due to mistakenly violating HTTP semantics.
+</t>
+<t><iref primary="true" item="interception proxy"/>
+<iref primary="true" item="transparent proxy"/>
+   For example, an
+   <x:dfn>interception proxy</x:dfn> <xref target="RFC3040"/> (also commonly
+   known as a <x:dfn>transparent proxy</x:dfn> <xref target="RFC1919"/>)
+   differs from an HTTP proxy because it is not chosen by the client.
+   Instead, an interception proxy filters or redirects outgoing TCP port 80
+   packets (and occasionally other common port traffic).
+   Interception proxies are commonly found on public network access points,
+   as a means of enforcing account subscription prior to allowing use of
+   non-local Internet services, and within corporate firewalls to enforce
+   network usage policies.
+</t>
+</section>
+
+<section title="Caches" anchor="caches">
+<iref primary="true" item="cache"/>
+<t>
+   A <x:dfn>cache</x:dfn> is a local store of previous response messages and the
+   subsystem that controls its message storage, retrieval, and deletion.
+   A cache stores cacheable responses in order to reduce the response
+   time and network bandwidth consumption on future, equivalent
+   requests. Any client or server <bcp14>MAY</bcp14> employ a cache, though a cache
+   cannot be used while acting as a tunnel.
+</t>
+<t>
+   The effect of a cache is that the request/response chain is shortened
+   if one of the participants along the chain has a cached response
+   applicable to that request. The following illustrates the resulting
+   chain if B has a cached copy of an earlier response from O (via C)
+   for a request that has not been cached by UA or A.
+</t>
+<figure><artwork type="drawing">
+            &gt;             &gt;
+       <x:highlight>UA</x:highlight> =========== <x:highlight>A</x:highlight> =========== <x:highlight>B</x:highlight> - - - - - - <x:highlight>C</x:highlight> - - - - - - <x:highlight>O</x:highlight>
+                  &lt;             &lt;
+</artwork></figure>
+<t><iref primary="true" item="cacheable"/>
+   A response is <x:dfn>cacheable</x:dfn> if a cache is allowed to store a copy of
+   the response message for use in answering subsequent requests.
+   Even when a response is cacheable, there might be additional
+   constraints placed by the client or by the origin server on when
+   that cached response can be used for a particular request. HTTP
+   requirements for cache behavior and cacheable responses are
+   defined in <xref target="CACHING"/>.
+</t>
+<t>
+   There is a wide variety of architectures and configurations
+   of caches deployed across the World Wide Web and
+   inside large organizations. These include national hierarchies
+   of proxy caches to save bandwidth and reduce latency, content delivery
+   networks that use gateway caching to optimize regional and global distribution of popular sites,
+   collaborative systems that
+   broadcast or multicast cache entries, archives of pre-fetched cache
+   entries for use in off-line or high-latency environments, and so on.
+</t>
+</section>
+
+<section title="Example Message Exchange" anchor="example">
+<t>
+   The following example illustrates a typical HTTP/1.1 message exchange for a
+   GET request (<xref target="GET"/>) on the URI "http://www.example.com/hello.txt":
+</t>
+<t>
+Client request:
+</t>
+<sourcecode type="http-message">
+GET /hello.txt HTTP/1.1
+User-Agent: curl/7.64.1
+Host: www.example.com
+Accept-Language: en, mi
+
+</sourcecode>
+<t>
+Server response:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 200 OK
+Date: Mon, 27 Jul 2009 12:28:53 GMT
+Server: Apache
+Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
+ETag: "34aa387-d-1568eb00"
+Accept-Ranges: bytes
+Content-Length: <x:length-of target="exbody"/>
+Vary: Accept-Encoding
+Content-Type: text/plain
+
+<x:span anchor="exbody">Hello World! My content includes a trailing CRLF.
+</x:span></sourcecode>
+</section>
+</section>
+
+<section title="Identifiers in HTTP" anchor="uri">
+  <iref primary="true" item="URI"/>
+  <iref primary="false" item="resource"/>
+<t>
+   Uniform Resource Identifiers (URIs) <xref target="URI"/> are used
+   throughout HTTP as the means for identifying resources (<xref target="resources"/>).
+</t>
+
+<section title="URI References" anchor="uri.references">
+  <iref primary="true" item="URI reference"/>
+  <x:anchor-alias value="URI-reference"/>
+  <x:anchor-alias value="absolute-URI"/>
+  <x:anchor-alias value="relative-part"/>
+  <x:anchor-alias value="authority"/>
+  <x:anchor-alias value="uri-host"/>
+  <x:anchor-alias value="port"/>
+  <x:anchor-alias value="path"/>
+  <x:anchor-alias value="path-abempty"/>
+  <x:anchor-alias value="segment"/>
+  <x:anchor-alias value="query"/>
+  <x:anchor-alias value="absolute-path"/>
+  <x:anchor-alias value="partial-URI"/>
+<t>
+   URI references are used to target requests, indicate redirects, and define
+   relationships.
+</t>
+<t>
+   The definitions of "URI-reference",
+   "absolute-URI", "relative-part", "authority", "port", "host",
+   "path-abempty", "segment", and "query" are adopted from the
+   URI generic syntax.
+   An "absolute-path" rule is defined for protocol elements that can contain a
+   non-empty path component. (This rule differs slightly from the path-abempty
+   rule of RFC 3986, which allows for an empty path,
+   and path-absolute rule, which does not allow paths that begin with "//".)
+   A "partial-URI" rule is defined for protocol elements
+   that can contain a relative URI but not a fragment component.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="URI-reference"><!--exported production--></iref><iref primary="true" item="Grammar" subitem="absolute-URI"/><iref primary="true" item="Grammar" subitem="authority"/><iref primary="true" item="Grammar" subitem="absolute-path"/><iref primary="true" item="Grammar" subitem="port"/><iref primary="true" item="Grammar" subitem="query"/><iref primary="true" item="Grammar" subitem="segment"/><iref primary="true" item="Grammar" subitem="uri-host"/><iref primary="true" item="Grammar" subitem="partial-URI"><!--exported production--></iref>
+  <x:ref>URI-reference</x:ref> = &lt;URI-reference, see <xref target="URI" x:fmt="," x:sec="4.1"/>&gt;
+  <x:ref>absolute-URI</x:ref>  = &lt;absolute-URI, see <xref target="URI" x:fmt="," x:sec="4.3"/>&gt;
+  <x:ref>relative-part</x:ref> = &lt;relative-part, see <xref target="URI" x:fmt="," x:sec="4.2"/>&gt;
+  <x:ref>authority</x:ref>     = &lt;authority, see <xref target="URI" x:fmt="," x:sec="3.2"/>&gt;
+  <x:ref>uri-host</x:ref>      = &lt;host, see <xref target="URI" x:fmt="," x:sec="3.2.2"/>&gt;
+  <x:ref>port</x:ref>          = &lt;port, see <xref target="URI" x:fmt="," x:sec="3.2.3"/>&gt;
+  <x:ref>path-abempty</x:ref>  = &lt;path-abempty, see <xref target="URI" x:fmt="," x:sec="3.3"/>&gt;
+  <x:ref>segment</x:ref>       = &lt;segment, see <xref target="URI" x:fmt="," x:sec="3.3"/>&gt;
+  <x:ref>query</x:ref>         = &lt;query, see <xref target="URI" x:fmt="," x:sec="3.4"/>&gt;
+
+  <x:ref>absolute-path</x:ref> = 1*( "/" segment )
+  <x:ref>partial-URI</x:ref>   = relative-part [ "?" query ]
+</sourcecode>
+<t>
+   Each protocol element in HTTP that allows a URI reference will indicate
+   in its ABNF production whether the element allows any form of reference
+   (URI-reference), only a URI in absolute form (absolute-URI), only the
+   path and optional query components (partial-URI),
+   or some combination of the above.
+   Unless otherwise indicated, URI references are parsed
+   relative to the target URI (<xref target="target.resource"/>).
+</t>
+<t>
+   It is <bcp14>RECOMMENDED</bcp14> that all senders and recipients support, at a minimum,
+   URIs with lengths of 8000 octets in protocol elements. Note that this
+   implies some structures and on-wire representations (for example, the
+   request line in HTTP/1.1) will necessarily be larger in some cases.
+</t>
+</section>
+
+<section title="HTTP-Related URI Schemes" anchor="uri.schemes">
+<t>
+   IANA maintains the registry of URI Schemes <xref target="BCP35"/> at
+   <eref target="https://www.iana.org/assignments/uri-schemes/"/>.
+   Although requests might target any URI scheme, the following schemes are
+   inherent to HTTP servers:
+</t>
+<table align="left" anchor="uri.scheme.table">
+  <thead>
+    <tr>
+      <th>URI Scheme</th>
+      <th>Description</th>
+      <th>Section</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>http</td>
+      <td>Hypertext Transfer Protocol</td>
+      <td><xref target="http.uri" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>https</td>
+      <td>Hypertext Transfer Protocol Secure</td>
+      <td><xref target="https.uri" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+<t>
+   Note that the presence of an "http" or "https" URI does not imply that
+   there is always an HTTP server at the identified origin listening for
+   connections. Anyone can mint a URI, whether or not a server exists and
+   whether or not that server currently maps that identifier to a resource.
+   The delegated nature of registered names and IP addresses creates a
+   federated namespace whether or not an HTTP server is present.
+</t>
+
+<section title="http URI Scheme" anchor="http.uri">
+  <x:anchor-alias value="http-URI"/>
+  <iref item="http URI scheme" primary="true"/>
+  <iref item="URI scheme" subitem="http" primary="true"/>
+<t>
+   The "http" URI scheme is hereby defined for minting identifiers within the
+   hierarchical namespace governed by a potential HTTP origin server
+   listening for TCP (<xref target="TCP"/>) connections on a given port.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="http-URI"><!--terminal production--></iref>
+  <x:ref>http-URI</x:ref> = "http" "://" <x:ref>authority</x:ref> <x:ref>path-abempty</x:ref> [ "?" <x:ref>query</x:ref> ]
+</sourcecode>
+<t>
+   The origin server for an "http" URI is identified by the
+   <x:ref>authority</x:ref> component, which includes a host identifier
+   (<xref target="URI" x:fmt="," x:sec="3.2.2"/>)
+   and optional port number (<xref target="URI" x:fmt="," x:sec="3.2.3"/>).
+   If the port subcomponent is empty or not given, TCP port 80 (the
+   reserved port for WWW services) is the default.
+   The origin determines who has the right to respond authoritatively to
+   requests that target the identified resource, as defined in
+   <xref target="http.origin"/>.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate an "http" URI with an empty host identifier.
+   A recipient that processes such a URI reference <bcp14>MUST</bcp14> reject it as invalid.
+</t>
+<t>
+   The hierarchical path component and optional query component identify the
+   target resource within that origin server's namespace.
+</t>
+</section>
+
+<section title="https URI Scheme" anchor="https.uri">
+   <x:anchor-alias value="https-URI"/>
+   <x:anchor-alias value="secured"/>
+   <iref item="https URI scheme" primary="true"/>
+   <iref item="URI scheme" subitem="https" primary="true"/>
+   <iref item="secured" primary="true"/>
+<t>
+   The "https" URI scheme is hereby defined for minting identifiers within the
+   hierarchical namespace governed by a potential origin server listening for
+   TCP connections on a given port and capable of establishing a TLS
+   (<xref target="TLS13"/>) connection that has been secured for HTTP
+   communication. In this context, <x:dfn>secured</x:dfn> specifically
+   means that the server has been authenticated as acting on behalf of the
+   identified authority and all HTTP communication with that server has
+   confidentiality and integrity protection that is acceptable to both client
+   and server.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="https-URI"><!--terminal production--></iref>
+  <x:ref>https-URI</x:ref> = "https" "://" <x:ref>authority</x:ref> <x:ref>path-abempty</x:ref> [ "?" <x:ref>query</x:ref> ]
+</sourcecode>
+<t>
+   The origin server for an "https" URI is identified by the
+   <x:ref>authority</x:ref> component, which includes a host identifier
+   (<xref target="URI" x:fmt="," x:sec="3.2.2"/>)
+   and optional port number (<xref target="URI" x:fmt="," x:sec="3.2.3"/>).
+   If the port subcomponent is empty or not given, TCP port 443
+   (the reserved port for HTTP over TLS) is the default.
+   The origin determines who has the right to respond authoritatively to
+   requests that target the identified resource, as defined in
+   <xref target="https.origin"/>.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate an "https" URI with an empty host identifier.
+   A recipient that processes such a URI reference <bcp14>MUST</bcp14> reject it as invalid.
+</t>
+<t>
+   The hierarchical path component and optional query component identify the
+   target resource within that origin server's namespace.
+</t>
+<t>
+   A client <bcp14>MUST</bcp14> ensure that its HTTP requests for an "https" resource are
+   secured, prior to being communicated, and that it only accepts secured
+   responses to those requests. Note that the definition of what cryptographic
+   mechanisms are acceptable to client and server are usually negotiated and
+   can change over time.
+</t>
+<t>
+   Resources made available via the "https" scheme have no shared identity
+   with the "http" scheme. They are distinct origins with separate namespaces.
+   However, extensions to HTTP that are defined as applying to all origins with
+   the same host, such as the Cookie protocol <xref target="COOKIE"/>,
+   allow information set by one service to impact communication with other
+   services within a matching group of host domains. Such extensions ought to
+   be designed with great care to prevent information obtained from a secured
+   connection being inadvertently exchanged within an unsecured context.
+</t>
+</section>
+
+<section title="http(s) Normalization and Comparison" anchor="uri.comparison">
+<t>
+   URIs with an "http" or "https" scheme are normalized and compared according to the
+   methods defined in <xref target="URI" x:fmt="of" x:sec="6"/>, using
+   the defaults described above for each scheme.
+</t>
+<t>
+   HTTP does not require the use of a specific method for determining
+   equivalence. For example, a cache key might be compared as a simple
+   string, after syntax-based normalization, or after scheme-based
+   normalization.
+</t>
+<t>
+   Scheme-based normalization (<xref target="URI" x:fmt="of" x:sec="6.2.3"/>) of "http" and "https" URIs involves the following
+   additional rules:
+</t>
+<ul>
+   <li>If the port is equal to the default port for a scheme, the normal form
+   is to omit the port subcomponent.</li>
+   <li>When not being used as the target of an OPTIONS request, an empty path
+   component is equivalent to an absolute path of "/", so the normal form is
+   to provide a path of "/" instead.</li>
+   <li>The scheme and host are case-insensitive and normally provided in
+   lowercase; all other components are compared in a case-sensitive
+   manner.</li>
+   <li>Characters other than those in the "reserved" set are equivalent to
+   their percent-encoded octets: the normal form is to not encode them (see
+   Sections <xref target="URI" x:fmt="number" x:sec="2.1"/> and <xref target="URI" x:fmt="number" x:sec="2.2"/> of <xref target="URI"/>).</li>
+</ul>
+<t>
+   For example, the following three URIs are equivalent:
+</t>
+<artwork type="example">
+   http://example.com:80/~smith/home.html
+   http://EXAMPLE.com/%7Esmith/home.html
+   http://EXAMPLE.com:/%7esmith/home.html
+</artwork>
+<t>
+   Two HTTP URIs that are equivalent after normalization (using any method)
+   can be assumed to identify the same resource, and any HTTP component <bcp14>MAY</bcp14>
+   perform normalization. As a result, distinct resources <bcp14>SHOULD NOT</bcp14> be
+   identified by HTTP URIs that are equivalent after normalization (using any
+   method defined in <xref target="URI" x:fmt="of" x:sec="6.2"/>).
+</t>
+</section>
+
+<section title="Deprecation of userinfo in http(s) URIs" anchor="http.userinfo">
+<t>
+   The URI generic syntax for authority also includes a userinfo subcomponent
+   (<xref target="URI" x:fmt="," x:sec="3.2.1"/>) for including user
+   authentication information in the URI. In that subcomponent, the
+   use of the format "user:password" is deprecated.
+</t>
+<t>
+   Some implementations make use of the userinfo component for internal
+   configuration of authentication information, such as within command
+   invocation options, configuration files, or bookmark lists, even
+   though such usage might expose a user identifier or password.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate the userinfo subcomponent (and its "@"
+   delimiter) when an "http" or "https" URI reference is generated within a
+   message as a target URI or field value.
+</t>
+<t>
+   Before making use of an "http" or "https" URI reference received from an untrusted
+   source, a recipient <bcp14>SHOULD</bcp14> parse for userinfo and treat its presence as
+   an error; it is likely being used to obscure the authority for the sake of
+   phishing attacks.
+</t>
+</section>
+
+<section title="http(s) References with Fragment Identifiers" anchor="uri.fragment.identifiers">
+  <iref item="Fragment Identifiers"/>
+<t>
+   Fragment identifiers allow for indirect identification
+   of a secondary resource, independent of the URI scheme, as defined in
+   <xref target="URI" x:fmt="of" x:sec="3.5"/>.
+   Some protocol elements that refer to a URI allow inclusion of a fragment,
+   while others do not. They are distinguished by use of the ABNF rule for
+   elements where fragment is allowed; otherwise, a specific rule that excludes
+   fragments is used.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> The fragment identifier component is not part of the scheme
+    definition for a URI scheme (see <xref target="URI" x:fmt="of" x:sec="4.3"/>),
+    thus does not appear in the ABNF definitions for the "http" and "https"
+    URI schemes above.
+  </t>
+</aside>
+</section>
+</section>
+
+<section title="Authoritative Access" anchor="authoritative.access">
+<t>
+   Authoritative access refers to dereferencing a given identifier,
+   for the sake of access to the identified resource, in a way that the client
+   believes is authoritative (controlled by the resource owner). The process
+   for determining whether access is granted is defined by the URI scheme and often uses
+   data within the URI components, such as the authority component when
+   the generic syntax is used. However, authoritative access is not limited to
+   the identified mechanism.
+</t>
+<t>
+   <xref target="origin"/> defines the concept of an origin as an aid to
+   such uses, and the subsequent subsections explain how to establish that a
+   peer has the authority to represent an origin.
+</t>
+<t>
+   See <xref target="establishing.authority"/> for security considerations
+   related to establishing authority.
+</t>
+
+<section title="URI Origin" anchor="origin">
+  <iref primary="true" item="origin"/>
+  <iref primary="true" item="URI" subitem="origin"/>
+<t>
+   The <x:dfn>origin</x:dfn> for a given URI is the triple of scheme, host,
+   and port after normalizing the scheme and host to lowercase and
+   normalizing the port to remove any leading zeros. If port is elided from
+   the URI, the default port for that scheme is used. For example, the URI
+</t>
+<artwork type="example">
+   https://Example.Com/happy.js
+</artwork>
+<t>
+   would have the origin
+</t>
+<artwork type="example">
+   { "https", "example.com", "443" }
+</artwork>
+<t>
+   which can also be described as the normalized URI prefix with port always
+   present:
+</t>
+<artwork type="example">
+   https://example.com:443
+</artwork>
+<t>
+   Each origin defines its own namespace and controls how identifiers
+   within that namespace are mapped to resources. In turn, how the origin
+   responds to valid requests, consistently over time, determines the
+   semantics that users will associate with a URI, and the usefulness of
+   those semantics is what ultimately transforms these mechanisms into a
+   resource for users to reference and access in the future.
+</t>
+<t>
+   Two origins are distinct if they differ in scheme, host, or port. Even
+   when it can be verified that the same entity controls two distinct origins,
+   the two namespaces under those origins are distinct unless explicitly
+   aliased by a server authoritative for that origin.
+</t>
+<t>
+   Origin is also used within HTML and related Web protocols, beyond the
+   scope of this document, as described in <xref target="RFC6454"/>.
+</t>
+</section>
+
+<section title="http Origins" anchor="http.origin">
+<t>
+   Although HTTP is independent of the transport protocol, the "http" scheme
+   (<xref target="http.uri"/>) is specific to associating authority with
+   whomever controls the origin
+   server listening for TCP connections on the indicated port of whatever
+   host is identified within the authority component. This is a very weak
+   sense of authority because it depends on both client-specific name
+   resolution mechanisms and communication that might not be secured from
+   an on-path attacker. Nevertheless, it is a sufficient minimum for
+   binding "http" identifiers to an origin server for consistent resolution
+   within a trusted environment.
+</t>
+<t>
+   If the host identifier is provided as an IP address, the origin server is
+   the listener (if any) on the indicated TCP port at that IP address.
+   If host is a registered name, the registered name is an indirect identifier
+   for use with a name resolution service, such as DNS, to find an address for
+   an appropriate origin server.
+</t>
+<t>
+   When an "http" URI is used within a context that calls for access to the
+   indicated resource, a client <bcp14>MAY</bcp14> attempt access by resolving the host
+   identifier to an IP address, establishing a TCP connection to that
+   address on the indicated port, and sending over that connection an HTTP
+   request message containing a request target that matches the client's
+   target URI (<xref target="target.resource"/>).
+</t>
+<t>
+   If the server responds to such a request with a non-interim HTTP response
+   message, as described in <xref target="status.codes"/>, then that response
+   is considered an authoritative answer to the client's request.
+</t>
+<t>
+   Note, however, that the above is not the only means for obtaining an
+   authoritative response, nor does it imply that an authoritative response
+   is always necessary (see <xref target="CACHING"/>).
+   For example, the Alt-Svc header field <xref target="ALTSVC"/> allows an
+   origin server to identify other services that are also authoritative for
+   that origin. Access to "http" identified resources might also be provided
+   by protocols outside the scope of this document.
+</t>
+</section>
+
+<section title="https Origins" anchor="https.origin">
+<t>
+   The "https" scheme (<xref target="https.uri"/>) associates authority based
+   on the ability of a server to use the private key corresponding to a
+   certificate that the client considers to be trustworthy for the identified
+   origin server. The client usually relies upon a chain of trust, conveyed
+   from some prearranged or configured trust anchor, to deem a certificate
+   trustworthy (<xref target="https.verify"/>).
+</t>
+<t>
+   In HTTP/1.1 and earlier, a client will only attribute authority to a server
+   when they are communicating over a successfully established and secured
+   connection specifically to that URI origin's host. The connection
+   establishment and certificate verification are used as proof of authority.
+</t>
+<t>
+   In HTTP/2 and HTTP/3, a client will attribute authority to a server when
+   they are communicating over a successfully established and secured
+   connection if the URI origin's host matches any of the hosts present in the
+   server's certificate and the client believes that it could open a connection
+   to that host for that URI. In practice, a client will make a DNS query to
+   check that the origin's host contains the same server IP address as the
+   established connection. This restriction can be removed by the origin server
+   sending an equivalent ORIGIN frame <xref target="RFC8336"/>.
+</t>
+<t>
+   The request target's host and port value are passed within each HTTP
+   request, identifying the origin and distinguishing it from other namespaces
+   that might be controlled by the same server (<xref target="field.host"/>).
+   It is the origin's responsibility to ensure that any services provided with
+   control over its certificate's private key are equally responsible for
+   managing the corresponding "https" namespaces or at least prepared to
+   reject requests that appear to have been misdirected
+   (<xref target="routing.reject"/>).
+</t>
+<t>
+   An origin server might be unwilling to process requests for certain target
+   URIs even when they have the authority to do so. For example, when a host
+   operates distinct services on different ports (e.g., 443 and 8000), checking
+   the target URI at the origin server is necessary (even after the connection
+   has been secured) because a network attacker might cause connections for one
+   port to be received at some other port. Failing to check the target URI
+   might allow such an attacker to replace a response to one target URI
+   (e.g., "https://example.com/foo") with a seemingly authoritative response
+   from the other port (e.g., "https://example.com:8000/foo").
+</t>
+<t>
+   Note that the "https" scheme does not rely on TCP and the connected port
+   number for associating authority, since both are outside the secured
+   communication and thus cannot be trusted as definitive. Hence, the HTTP
+   communication might take place over any channel that has been secured,
+   as defined in <xref target="https.uri"/>, including protocols that don't
+   use TCP.
+</t>
+<t>
+   When an "https" URI is used within a context that calls for access to
+   the indicated resource, a client <bcp14>MAY</bcp14> attempt access by resolving the
+   host identifier to an IP address, establishing a TCP connection to that
+   address on the indicated port, securing the connection end-to-end by
+   successfully initiating TLS over TCP with confidentiality and integrity
+   protection, and sending over that connection an HTTP request message
+   containing a request target that matches the client's target URI
+   (<xref target="target.resource"/>).
+</t>
+<t>
+   If the server responds to such a request with a non-interim HTTP response
+   message, as described in <xref target="status.codes"/>, then that response
+   is considered an authoritative answer to the client's request.
+</t>
+<t>
+   Note, however, that the above is not the only means for obtaining an
+   authoritative response, nor does it imply that an authoritative response
+   is always necessary (see <xref target="CACHING"/>).
+</t>
+</section>
+
+<section title="https Certificate Verification" anchor="https.verify">
+<t>
+   To establish a <x:ref>secured</x:ref> connection to dereference a URI,
+   a client <bcp14>MUST</bcp14> verify that the service's identity is an acceptable
+   match for the URI's origin server. Certificate verification is used to
+   prevent server impersonation by an on-path attacker or by an attacker
+   that controls name resolution. This process requires that a client be
+   configured with a set of trust anchors.
+</t>
+<t>
+   In general, a client <bcp14>MUST</bcp14> verify the service identity using the
+   verification process defined in
+   <xref target="RFC6125" x:fmt="of" x:sec="6"/>. The client <bcp14>MUST</bcp14> construct
+   a reference identity from the service's host: if the host is a literal IP address
+   (<xref target="https.ip-id"/>), the reference identity is an IP-ID, otherwise
+   the host is a name and the reference identity is a DNS-ID.
+</t>
+<t>
+   A reference identity of type CN-ID <bcp14>MUST NOT</bcp14> be used by clients.  As noted
+   in <xref target="RFC6125" x:fmt="of" x:sec="6.2.1"/>, a reference
+   identity of type CN-ID might be used by older clients.
+</t>
+<t>
+   A client might be specially configured to accept an alternative form of
+   server identity verification. For example, a client might be connecting
+   to a server whose address and hostname are dynamic, with an expectation that
+   the service will present a specific certificate (or a certificate matching
+   some externally defined reference identity) rather than one matching the
+   target URI's origin.
+</t>
+<t>
+   In special cases, it might be appropriate for
+   a client to simply ignore the server's identity, but it must be
+   understood that this leaves a connection open to active attack.
+</t>
+<t>
+   If the certificate is not valid for the target URI's origin,
+   a user agent <bcp14>MUST</bcp14> either obtain confirmation from the user
+   before proceeding (see <xref target="user.agent"/>) or
+   terminate the connection with a bad certificate error. Automated
+   clients <bcp14>MUST</bcp14> log the error to an appropriate audit log (if available)
+   and <bcp14>SHOULD</bcp14> terminate the connection (with a bad certificate error).
+   Automated clients <bcp14>MAY</bcp14> provide a configuration setting that disables
+   this check, but <bcp14>MUST</bcp14> provide a setting which enables it.
+</t>
+</section>
+<section title="IP-ID Reference Identity" anchor="https.ip-id">
+<t>
+   A server that is identified using an IP address literal in the "host" field
+   of an "https" URI has a reference identity of type IP-ID.  An IP version 4
+   address uses the "IPv4address" ABNF rule, and an IP version 6 address uses
+   the "IP-literal" production with the "IPv6address" option; see
+   <xref target="URI" x:fmt="of" x:sec="3.2.2"/>.  A reference identity of
+   IP-ID contains the decoded bytes of the IP address.
+</t>
+<t>
+   An IP version 4 address is 4 octets, and an IP version 6 address is 16 octets.
+   Use of IP-ID is not defined for any other IP version. The iPAddress
+   choice in the certificate subjectAltName extension does not explicitly
+   include the IP version and so relies on the length of the address to
+   distinguish versions; see
+   <xref target="RFC5280" x:fmt="of" x:sec="4.2.1.6"/>.
+</t>
+<t>
+   A reference identity of type IP-ID matches if the address is identical to
+   an iPAddress value of the subjectAltName extension of the certificate.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Fields" anchor="fields">
+<iref primary="true" item="field"/>
+<t>
+   HTTP uses <x:dfn>fields</x:dfn> to provide data in the form of extensible
+   name/value pairs with a registered key namespace. Fields are sent and
+   received within the header and trailer sections of messages
+   (<xref target="message.abstraction"/>).
+</t>
+
+<section title="Field Names" anchor="fields.names">
+  <x:anchor-alias value="header.names"/>
+  <x:anchor-alias value="field-name"/>
+<t>
+   A field name labels the corresponding field value as having the
+   semantics defined by that name.  For example, the <x:ref>Date</x:ref>
+   header field is defined in <xref target="field.date"/> as containing the
+   origination timestamp for the message in which it appears.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="field-name"/>
+  <x:ref>field-name</x:ref>     = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   Field names are case-insensitive and ought to be registered within the
+   "Hypertext Transfer Protocol (HTTP) Field Name Registry"; see <xref target="fields.registry"/>.
+</t>
+<t>
+   The interpretation of a field does not change between minor
+   versions of the same major HTTP version, though the default behavior of a
+   recipient in the absence of such a field can change. Unless specified
+   otherwise, fields are defined for all versions of HTTP.
+   In particular, the <x:ref>Host</x:ref> and <x:ref>Connection</x:ref>
+   fields ought to be recognized by all HTTP implementations
+   whether or not they advertise conformance with HTTP/1.1.
+</t>
+<t>
+   New fields can be introduced without changing the protocol version if
+   their defined semantics allow them to be safely ignored by recipients
+   that do not recognize them; see <xref target="fields.extensibility"/>.
+</t>
+<t>
+   A proxy <bcp14>MUST</bcp14> forward unrecognized header fields unless the
+   field name is listed in the <x:ref>Connection</x:ref> header field
+   (<xref target="field.connection"/>) or the proxy is specifically
+   configured to block, or otherwise transform, such fields.
+   Other recipients <bcp14>SHOULD</bcp14> ignore unrecognized header and trailer fields.
+   Adhering to these requirements allows HTTP's functionality to be extended
+   without updating or removing deployed intermediaries.
+</t>
+</section>
+
+<section title="Field Lines and Combined Field Value" anchor="field.lines">
+<t>
+   <iref item="field line"/>
+   <iref item="field name"/>
+   <iref item="field line value"/>
+   Field sections are composed of any number of <x:dfn>field lines</x:dfn>,
+   each with a <x:dfn>field name</x:dfn> (see <xref target="fields.names"/>)
+   identifying the field, and a <x:dfn>field line value</x:dfn> that conveys
+   data for that instance of the field.
+</t>
+<t>
+   <iref item="field value"/>
+   When a field name is only present once in a section, the combined
+   <x:dfn>field value</x:dfn> for that field consists of the corresponding
+   field line value.
+   When a field name is repeated within a section, its combined field value
+   consists of the list of corresponding field line values within that section,
+   concatenated in order, with each field line value separated by a comma.
+</t>
+<t>
+   For example, this section:
+</t>
+<sourcecode type="http-message">
+Example-Field: Foo, Bar
+Example-Field: Baz
+</sourcecode>
+<t>
+   contains two field lines, both with the field name "Example-Field". The
+   first field line has a field line value of "Foo, Bar", while the second
+   field line value is "Baz". The field value for "Example-Field" is the list
+   "Foo, Bar, Baz".
+</t>
+</section>
+
+<section title="Field Order" anchor="fields.order">
+  <x:anchor-alias value="header.order"/>
+<t>
+   A recipient <bcp14>MAY</bcp14> combine multiple field lines within a field section that
+   have the same field name
+   into one field line, without changing the semantics of the message, by
+   appending each subsequent field line value to the initial field line value
+   in order, separated by a comma (",") and optional whitespace
+   (<x:ref>OWS</x:ref>, defined in <xref target="whitespace"/>).
+   For consistency, use comma SP.
+</t>
+<t>
+   The order in which field lines with the
+   same name are received is therefore significant to the interpretation of
+   the field value; a proxy <bcp14>MUST NOT</bcp14> change the order of these field line
+   values when forwarding a message.
+</t>
+<t>
+   This means that, aside from the well-known exception noted below, a sender
+   <bcp14>MUST NOT</bcp14> generate multiple field lines with the same name in a message
+   (whether in the headers or trailers) or append a field line when a field
+   line of the same name already exists in the message, unless that field's
+   definition allows multiple field line values to be recombined as a
+   comma-separated list (i.e., at least one alternative of the field's
+   definition allows a comma-separated list, such as an ABNF rule of
+   #(values) defined in <xref target="abnf.extension"/>).
+</t>
+<aside>
+  <t>
+   <x:h>Note:</x:h> In practice, the "Set-Cookie" header field (<xref target="COOKIE"/>)
+   often appears in a response message across multiple field lines and does not
+   use the list syntax, violating the above requirements on multiple field lines
+   with the same field name. Since it cannot be combined into a single field
+   value, recipients ought to handle "Set-Cookie" as a special case while
+   processing fields. (See Appendix A.2.3 of <xref target="Kri2001"/> for
+   details.)
+  </t>
+</aside>
+<t>
+   The order in which field lines with differing field names are received in a
+   section is not significant. However, it is good practice to send header
+   fields that contain additional control data first, such as
+   <x:ref>Host</x:ref> on requests and <x:ref>Date</x:ref> on responses, so
+   that implementations can decide when not to handle a message as early as
+   possible.
+</t>
+<t>
+   A server <bcp14>MUST NOT</bcp14> apply a request to the target resource until it
+   receives the entire request header section, since later header field lines
+   might include conditionals, authentication credentials, or deliberately
+   misleading duplicate header fields that could impact request processing.
+</t>
+</section>
+
+<section title="Field Limits" anchor="fields.limits">
+  <x:anchor-alias value="header.limits"/>
+<t>
+   HTTP does not place a predefined limit on the length of each field line, field value,
+   or on the length of a header or trailer section as a whole, as described in
+   <xref target="conformance"/>. Various ad hoc limitations on individual
+   lengths are found in practice, often depending on the specific
+   field's semantics.
+</t>
+<t>
+   A server that receives a request header field line, field value, or set of
+   fields larger than it wishes to process <bcp14>MUST</bcp14> respond with an appropriate
+   <x:ref>4xx (Client Error)</x:ref> status code. Ignoring such header fields
+   would increase the server's vulnerability to request smuggling attacks
+   (<xref target="HTTP11" x:rel="#request.smuggling"/>).
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> discard or truncate received field lines that are larger
+   than the client wishes to process if the field semantics are such that the
+   dropped value(s) can be safely ignored without changing the
+   message framing or response semantics.
+</t>
+</section>
+
+<section title="Field Values" anchor="fields.values">
+  <x:anchor-alias value="header.values"/>
+  <x:anchor-alias value="field-value"/>
+  <x:anchor-alias value="field-content"/>
+  <x:anchor-alias value="field-vchar"/>
+  <x:anchor-alias value="singleton.field"/>
+  <x:anchor-alias value="list.field"/>
+  <x:anchor-alias value="obs-text"/>
+<t>
+   HTTP field values consist of a sequence of characters in a format defined
+   by the field's grammar. Each field's grammar is usually defined using
+   ABNF (<xref target="RFC5234"/>).
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="field-value"/><iref primary="true" item="Grammar" subitem="field-vchar"/><iref primary="true" item="Grammar" subitem="field-content"/><iref primary="true" item="Grammar" subitem="obs-text"/>
+  <x:ref>field-value</x:ref>    = *<x:ref>field-content</x:ref>
+  <x:ref>field-content</x:ref>  = <x:ref>field-vchar</x:ref>
+                   [ 1*( <x:ref>SP</x:ref> / <x:ref>HTAB</x:ref> / <x:ref>field-vchar</x:ref> ) <x:ref>field-vchar</x:ref> ]
+  <x:ref>field-vchar</x:ref>    = <x:ref>VCHAR</x:ref> / <x:ref>obs-text</x:ref>
+  <x:ref>obs-text</x:ref>       = %x80-FF
+</sourcecode>
+<t>
+   A field value does not include leading or trailing whitespace. When a
+   specific version of HTTP allows such whitespace to appear in a message,
+   a field parsing implementation <bcp14>MUST</bcp14> exclude such whitespace prior to
+   evaluating the field value.
+</t>
+<t>
+   Field values are usually constrained to the range of US-ASCII characters
+   <xref target="USASCII"/>.
+   Fields needing a greater range of characters can use an encoding,
+   such as the one defined in <xref target="RFC8187"/>.
+   Historically, HTTP allowed field content with text in the ISO-8859-1
+   charset <xref target="ISO-8859-1"/>, supporting other charsets only
+   through use of <xref target="RFC2047"/> encoding.
+   Specifications for newly defined fields <bcp14>SHOULD</bcp14> limit their values to
+   visible US-ASCII octets (VCHAR), SP, and HTAB.
+   A recipient <bcp14>SHOULD</bcp14> treat other allowed octets in field content
+   (i.e., <x:ref>obs-text</x:ref>) as opaque data.
+</t>
+<t>
+   Field values containing CR, LF, or NUL characters are invalid and dangerous,
+   due to the varying ways that implementations might parse and interpret
+   those characters; a recipient of CR, LF, or NUL within a field value <bcp14>MUST</bcp14>
+   either reject the message or replace each of those characters with SP
+   before further processing or forwarding of that message. Field values
+   containing other CTL characters are also invalid; however,
+   recipients <bcp14>MAY</bcp14> retain such characters for the sake of robustness when
+   they appear within a safe context (e.g., an application-specific quoted
+   string that will not be processed by any downstream HTTP parser).
+</t>
+<t><iref item="singleton field"/>
+   Fields that only anticipate a single member as the field value are
+   referred to as <x:dfn>singleton fields</x:dfn>.
+</t>
+<t><iref item="list-based field"/>
+   Fields that allow multiple members as the field value are referred to as
+   <x:dfn>list-based fields</x:dfn>. The list operator extension of
+   <xref target="abnf.extension"/> is used as a common notation for defining
+   field values that can contain multiple members.
+</t>
+<t>
+   Because commas (",") are used as the delimiter between members, they need
+   to be treated with care if they are allowed as data within a member. This
+   is true for both list-based and singleton fields, since a singleton field
+   might be erroneously sent with multiple members and detecting such errors
+   improves interoperability. Fields that expect to contain a
+   comma within a member, such as within an <x:ref>HTTP-date</x:ref> or
+   <x:ref>URI-reference</x:ref>
+   element, ought to be defined with delimiters around that element to
+   distinguish any comma within that data from potential list separators.
+</t>
+<t>
+   For example, a textual date and a URI (either of which might contain a comma)
+   could be safely carried in list-based field values like these:
+</t>
+<sourcecode type="http-message">
+Example-URIs: "http://example.com/a.html,foo",
+              "http://without-a-comma.example.com/"
+Example-Dates: "Sat, 04 May 1996", "Wed, 14 Sep 2005"
+</sourcecode>
+<t>
+   Note that double-quote delimiters are almost always used with the
+   quoted-string production (<xref target="quoted.strings"/>); using a different syntax inside double-quotes
+   will likely cause unnecessary confusion.
+</t>
+<t>
+   Many fields (such as <x:ref>Content-Type</x:ref>, defined in
+   <xref target="field.content-type"/>) use a common syntax for parameters
+   that allows both unquoted (token) and quoted (quoted-string) syntax for
+   a parameter value (<xref target="parameter"/>). Use of common syntax
+   allows recipients to reuse existing parser components. When allowing both
+   forms, the meaning of a parameter value ought to be the same whether it
+   was received as a token or a quoted string.
+</t>
+<aside>
+   <t>
+      <x:h>Note:</x:h> For defining field value syntax, this specification uses an ABNF
+      rule named after the field name to define the allowed grammar for that
+      field's value (after said value has been extracted from the underlying
+      messaging syntax and multiple instances combined into a list).
+   </t>
+</aside>
+</section>
+
+<section title="Common Rules for Defining Field Values" anchor="fields.components">
+  <x:anchor-alias value="header.components"/>
+
+<section title="Lists (#rule ABNF Extension)" anchor="abnf.extension">
+<t>
+   A #rule extension to the ABNF rules of <xref target="RFC5234"/> is used to
+   improve readability in the definitions of some list-based field values.
+</t>
+<t>
+   A construct "#" is defined, similar to "*", for defining comma-delimited
+   lists of elements. The full form is "&lt;n&gt;#&lt;m&gt;element" indicating
+   at least &lt;n&gt; and at most &lt;m&gt; elements, each separated by a single
+   comma (",") and optional whitespace (<x:ref>OWS</x:ref>,
+   defined in <xref target="whitespace"/>).
+</t>
+
+<section title="Sender Requirements" anchor="abnf.extension.sender">
+<t>
+   In any production that uses the list construct, a sender <bcp14>MUST NOT</bcp14>
+   generate empty list elements. In other words, a sender has to generate
+   lists that satisfy the following syntax:
+</t>
+<artwork type="example">
+  1#element =&gt; element *( OWS "," OWS element )
+</artwork>
+<t>
+   and:
+</t>
+<artwork type="example">
+  #element =&gt; [ 1#element ]
+</artwork>
+<t>
+   and for n &gt;= 1 and m &gt; 1:
+</t>
+<artwork type="example">
+  &lt;n&gt;#&lt;m&gt;element =&gt; element &lt;n-1&gt;*&lt;m-1&gt;( OWS "," OWS element )
+</artwork>
+<t>
+   <xref target="collected.abnf"/> shows the collected ABNF for senders
+   after the list constructs have been expanded.
+</t>
+</section>
+
+<section title="Recipient Requirements" anchor="abnf.extension.recipient">
+<t>
+   Empty elements do not contribute to the count of elements present.
+   A recipient <bcp14>MUST</bcp14> parse and ignore
+   a reasonable number of empty list elements: enough to handle common mistakes
+   by senders that merge values, but not so much that they could be used as a
+   denial-of-service mechanism. In other words, a recipient <bcp14>MUST</bcp14> accept lists
+   that satisfy the following syntax:
+</t>
+<artwork type="example">
+  #element =&gt; [ element ] *( OWS "," OWS [ element ] )
+</artwork>
+<t>
+   Note that because of the potential presence of empty list elements, the
+   RFC 5234 ABNF cannot enforce the cardinality of list elements, and
+   consequently all cases are mapped as if there was no cardinality specified.
+</t>
+<t>
+   For example, given these ABNF productions:
+</t>
+<artwork type="example">
+  example-list      = 1#example-list-elmt
+  example-list-elmt = token ; see <xref target="tokens"/>
+</artwork>
+<t>
+   Then the following are valid values for example-list (not including the
+   double quotes, which are present for delimitation only):
+</t>
+<artwork type="example">
+  "foo,bar"
+  "foo ,bar,"
+  "foo , ,bar,charlie"
+</artwork>
+<t>
+   In contrast, the following values would be invalid, since at least one
+   non-empty element is required by the example-list production:
+</t>
+<artwork type="example">
+  ""
+  ","
+  ",   ,"
+</artwork>
+</section>
+</section>
+
+<section title="Tokens" anchor="tokens">
+<t anchor="rule.token.separators">
+  <x:anchor-alias value="tchar"/>
+  <x:anchor-alias value="token"/>
+  Tokens are short textual identifiers that do not include whitespace or
+  delimiters.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="token"/><iref primary="true" item="Grammar" subitem="tchar"/>
+  <x:ref>token</x:ref>          = 1*<x:ref>tchar</x:ref>
+<!--
+  NOTE: the definition of tchar and the prose above about special characters need to match!
+ -->
+  <x:ref>tchar</x:ref>          = "!" / "#" / "$" / "%" / "&amp;" / "'" / "*"
+                 / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
+                 / <x:ref>DIGIT</x:ref> / <x:ref>ALPHA</x:ref>
+                 ; any <x:ref>VCHAR</x:ref>, except delimiters
+</sourcecode>
+<t anchor="delimiters">
+   <iref item="Delimiters"/>
+   Many HTTP field values are defined using common syntax
+   components, separated by whitespace or specific delimiting characters.
+   Delimiters are chosen from the set of US-ASCII visual characters not
+   allowed in a <x:ref>token</x:ref> (DQUOTE and "(),/:;&lt;=&gt;?@[\]{}").
+</t>
+</section>
+
+<section title="Whitespace" anchor="whitespace">
+  <x:anchor-alias value="BWS"/>
+  <x:anchor-alias value="OWS"/>
+  <x:anchor-alias value="RWS"/>
+<t>
+   This specification uses three rules to denote the use of linear
+   whitespace: OWS (optional whitespace), RWS (required whitespace), and
+   BWS ("bad" whitespace).
+</t>
+<t>
+   The OWS rule is used where zero or more linear whitespace octets might
+   appear. For protocol elements where optional whitespace is preferred to
+   improve readability, a sender <bcp14>SHOULD</bcp14> generate the optional whitespace
+   as a single SP; otherwise, a sender <bcp14>SHOULD NOT</bcp14> generate optional
+   whitespace except as needed to overwrite invalid or unwanted protocol
+   elements during in-place message filtering.
+</t>
+<t>
+   The RWS rule is used when at least one linear whitespace octet is required
+   to separate field tokens. A sender <bcp14>SHOULD</bcp14> generate RWS as a single SP.
+</t>
+<t>
+   OWS and RWS have the same semantics as a single SP. Any content known to
+   be defined as OWS or RWS <bcp14>MAY</bcp14> be replaced with a single SP before
+   interpreting it or forwarding the message downstream.
+</t>
+<t>
+   The BWS rule is used where the grammar allows optional whitespace only for
+   historical reasons. A sender <bcp14>MUST NOT</bcp14> generate BWS in messages.
+   A recipient <bcp14>MUST</bcp14> parse for such bad whitespace and remove it before
+   interpreting the protocol element.
+</t>
+<t>
+   BWS has no semantics. Any content known to be
+   defined as BWS <bcp14>MAY</bcp14> be removed before interpreting it or forwarding the
+   message downstream.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="OWS"/><iref primary="true" item="Grammar" subitem="RWS"/><iref primary="true" item="Grammar" subitem="BWS"/>
+  <x:ref>OWS</x:ref>            = *( <x:ref>SP</x:ref> / <x:ref>HTAB</x:ref> )
+                 ; optional whitespace
+  <x:ref>RWS</x:ref>            = 1*( <x:ref>SP</x:ref> / <x:ref>HTAB</x:ref> )
+                 ; required whitespace
+  <x:ref>BWS</x:ref>            = <x:ref>OWS</x:ref>
+                 ; "bad" whitespace
+</sourcecode>
+</section>
+
+<section title="Quoted Strings" anchor="quoted.strings">
+<t anchor="rule.quoted-string">
+  <x:anchor-alias value="quoted-string"/>
+  <x:anchor-alias value="qdtext"/>
+   A string of text is parsed as a single value if it is quoted using
+   double-quote marks.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="quoted-string"/><iref primary="true" item="Grammar" subitem="qdtext"/>
+  <x:ref>quoted-string</x:ref>  = <x:ref>DQUOTE</x:ref> *( <x:ref>qdtext</x:ref> / <x:ref>quoted-pair</x:ref> ) <x:ref>DQUOTE</x:ref>
+  <x:ref>qdtext</x:ref>         = <x:ref>HTAB</x:ref> / <x:ref>SP</x:ref> / %x21 / %x23-5B / %x5D-7E / <x:ref>obs-text</x:ref>
+</sourcecode>
+<t anchor="rule.quoted-pair">
+  <x:anchor-alias value="quoted-pair"/>
+   The backslash octet ("\") can be used as a single-octet
+   quoting mechanism within quoted-string and comment constructs.
+   Recipients that process the value of a quoted-string <bcp14>MUST</bcp14> handle a
+   quoted-pair as if it were replaced by the octet following the backslash.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="quoted-pair"/>
+  <x:ref>quoted-pair</x:ref>    = "\" ( <x:ref>HTAB</x:ref> / <x:ref>SP</x:ref> / <x:ref>VCHAR</x:ref> / <x:ref>obs-text</x:ref> )
+</sourcecode>
+<t>
+   A sender <bcp14>SHOULD NOT</bcp14> generate a quoted-pair in a quoted-string except
+   where necessary to quote DQUOTE and backslash octets occurring within that
+   string.
+   A sender <bcp14>SHOULD NOT</bcp14> generate a quoted-pair in a comment except
+   where necessary to quote parentheses ["(" and ")"] and backslash octets
+   occurring within that comment.
+</t>
+</section>
+
+<section title="Comments" anchor="comments">
+<t anchor="rule.comment">
+  <x:anchor-alias value="comment"/>
+  <x:anchor-alias value="ctext"/>
+   Comments can be included in some HTTP fields by surrounding
+   the comment text with parentheses. Comments are only allowed in
+   fields containing "comment" as part of their field value definition.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="comment"/><iref primary="true" item="Grammar" subitem="ctext"/>
+  <x:ref>comment</x:ref>        = "(" *( <x:ref>ctext</x:ref> / <x:ref>quoted-pair</x:ref> / <x:ref>comment</x:ref> ) ")"
+  <x:ref>ctext</x:ref>          = <x:ref>HTAB</x:ref> / <x:ref>SP</x:ref> / %x21-27 / %x2A-5B / %x5D-7E / <x:ref>obs-text</x:ref>
+</sourcecode>
+</section>
+
+<section title="Parameters" anchor="parameter">
+<t anchor="rule.parameter">
+   <x:anchor-alias value="parameters"/>
+   <x:anchor-alias value="parameter-name"/>
+   <x:anchor-alias value="parameter-value"/>
+   Parameters are instances of name/value pairs; they are often used in field
+   values as a common syntax for appending auxiliary information to an item.
+   Each parameter is usually delimited by an immediately preceding semicolon.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="parameters"/><iref primary="true" item="Grammar" subitem="parameter"/><iref primary="true" item="Grammar" subitem="parameter-name"/><iref primary="true" item="Grammar" subitem="parameter-value"/>
+  <x:ref>parameters</x:ref>      = *( OWS ";" OWS [ <x:ref>parameter</x:ref> ] )
+  <x:ref>parameter</x:ref>       = <x:ref>parameter-name</x:ref> "=" <x:ref>parameter-value</x:ref>
+  <x:ref>parameter-name</x:ref>  = <x:ref>token</x:ref>
+  <x:ref>parameter-value</x:ref> = ( <x:ref>token</x:ref> / <x:ref>quoted-string</x:ref> )
+</sourcecode>
+<t>
+   Parameter names are case-insensitive. Parameter values might or might
+   not be case-sensitive, depending on the semantics of the parameter
+   name. Examples of parameters and some equivalent forms can be seen in
+   media types (<xref target="media.type"/>) and the Accept header field
+   (<xref target="field.accept"/>).
+</t>
+<t>
+   A parameter value that matches the <x:ref>token</x:ref> production can be
+   transmitted either as a token or within a quoted-string. The quoted and
+   unquoted values are equivalent.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Parameters do not allow whitespace (not even "bad" whitespace)
+    around the "=" character.
+  </t>
+</aside>
+</section>
+
+<section title="Date/Time Formats" anchor="http.date">
+  <x:anchor-alias value="HTTP-date"/>
+  <x:anchor-alias value="clock"/>
+  <iref primary="true" item="clock"/>
+<t>
+   Prior to 1995, there were three different formats commonly used by servers
+   to communicate timestamps.  For compatibility with old implementations, all
+   three are defined here. The preferred format is a fixed-length and
+   single-zone subset of the date and time specification used by the
+   Internet Message Format <xref target="RFC5322"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="HTTP-date"/>
+  <x:ref>HTTP-date</x:ref>    = <x:ref>IMF-fixdate</x:ref> / <x:ref>obs-date</x:ref>
+</sourcecode>
+<t>
+   An example of the preferred format is
+</t>
+<artwork type="example" x:indent-with="  ">
+Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate
+</artwork>
+<t>
+   Examples of the two obsolete formats are
+</t>
+<artwork type="example" x:indent-with="  ">
+Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format
+Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format
+</artwork>
+<t>
+   A recipient that parses a timestamp value in an HTTP field <bcp14>MUST</bcp14>
+   accept all three HTTP-date formats. When a sender generates a field
+   that contains one or more timestamps defined as HTTP-date,
+   the sender <bcp14>MUST</bcp14> generate those timestamps in the IMF-fixdate format.
+</t>
+<t>
+   An HTTP-date value represents time as an instance of Coordinated
+   Universal Time (UTC). The first two formats indicate UTC by the
+   three-letter abbreviation for Greenwich Mean Time, "GMT", a predecessor
+   of the UTC name; values in the asctime format are assumed to be in UTC.
+</t>
+<t>
+   A <x:dfn>clock</x:dfn> is an implementation capable of providing a
+   reasonable approximation of the current instant in UTC.
+   A clock implementation ought to use NTP (<xref target="RFC5905"/>),
+   or some similar protocol, to synchronize with UTC.
+</t>
+<t anchor="preferred.date.format">
+  <x:anchor-alias value="IMF-fixdate"/>
+  <x:anchor-alias value="time-of-day"/>
+  <x:anchor-alias value="hour"/>
+  <x:anchor-alias value="minute"/>
+  <x:anchor-alias value="second"/>
+  <x:anchor-alias value="day-name"/>
+  <x:anchor-alias value="day"/>
+  <x:anchor-alias value="month"/>
+  <x:anchor-alias value="year"/>
+  <x:anchor-alias value="GMT"/>
+  Preferred format:
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="IMF-fixdate"/><iref primary="true" item="Grammar" subitem="date1"/><iref primary="true" item="Grammar" subitem="time-of-day"/><iref primary="true" item="Grammar" subitem="hour"/><iref primary="true" item="Grammar" subitem="minute"/><iref primary="true" item="Grammar" subitem="second"/><iref primary="true" item="Grammar" subitem="day-name"/><iref primary="true" item="Grammar" subitem="day-name-l"/><iref primary="true" item="Grammar" subitem="day"/><iref primary="true" item="Grammar" subitem="month"/><iref primary="true" item="Grammar" subitem="year"/><iref primary="true" item="Grammar" subitem="GMT"/>
+  <x:ref>IMF-fixdate</x:ref>  = <x:ref>day-name</x:ref> "," <x:ref>SP</x:ref> date1 <x:ref>SP</x:ref> <x:ref>time-of-day</x:ref> <x:ref>SP</x:ref> <x:ref>GMT</x:ref>
+  ; fixed length/zone/capitalization subset of the format
+  ; see <xref target="RFC5322" x:fmt="of" x:sec="3.3"/>
+
+  <x:ref>day-name</x:ref>     = %s"Mon" / %s"Tue" / %s"Wed"
+               / %s"Thu" / %s"Fri" / %s"Sat" / %s"Sun"
+
+  <x:ref>date1</x:ref>        = <x:ref>day</x:ref> <x:ref>SP</x:ref> <x:ref>month</x:ref> <x:ref>SP</x:ref> <x:ref>year</x:ref>
+               ; e.g., 02 Jun 1982
+
+  <x:ref>day</x:ref>          = 2<x:ref>DIGIT</x:ref>
+  <x:ref>month</x:ref>        = %s"Jan" / %s"Feb" / %s"Mar" / %s"Apr"
+               / %s"May" / %s"Jun" / %s"Jul" / %s"Aug"
+               / %s"Sep" / %s"Oct" / %s"Nov" / %s"Dec"
+  <x:ref>year</x:ref>         = 4<x:ref>DIGIT</x:ref>
+
+  <x:ref>GMT</x:ref>          = %s"GMT"
+
+  <x:ref>time-of-day</x:ref>  = <x:ref>hour</x:ref> ":" <x:ref>minute</x:ref> ":" <x:ref>second</x:ref>
+               ; 00:00:00 - 23:59:60 (leap second)
+
+  <x:ref>hour</x:ref>         = 2<x:ref>DIGIT</x:ref>
+  <x:ref>minute</x:ref>       = 2<x:ref>DIGIT</x:ref>
+  <x:ref>second</x:ref>       = 2<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t anchor="obsolete.date.formats">
+  <x:anchor-alias value="obs-date"/>
+  <x:anchor-alias value="rfc850-date"/>
+  <x:anchor-alias value="asctime-date"/>
+  <x:anchor-alias value="date1"/>
+  <x:anchor-alias value="date2"/>
+  <x:anchor-alias value="date3"/>
+  <x:anchor-alias value="day-name-l"/>
+  Obsolete formats:
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="obs-date"/>
+  <x:ref>obs-date</x:ref>     = <x:ref>rfc850-date</x:ref> / <x:ref>asctime-date</x:ref>
+</sourcecode>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="rfc850-date"/>
+  <x:ref>rfc850-date</x:ref>  = <x:ref>day-name-l</x:ref> "," <x:ref>SP</x:ref> <x:ref>date2</x:ref> <x:ref>SP</x:ref> <x:ref>time-of-day</x:ref> <x:ref>SP</x:ref> <x:ref>GMT</x:ref>
+  <x:ref>date2</x:ref>        = <x:ref>day</x:ref> "-" <x:ref>month</x:ref> "-" 2<x:ref>DIGIT</x:ref>
+               ; e.g., 02-Jun-82
+
+  <x:ref>day-name-l</x:ref>   = %s"Monday" / %s"Tuesday" / %s"Wednesday"
+               / %s"Thursday" / %s"Friday" / %s"Saturday"
+               / %s"Sunday"
+</sourcecode>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="asctime-date"/>
+  <x:ref>asctime-date</x:ref> = <x:ref>day-name</x:ref> <x:ref>SP</x:ref> <x:ref>date3</x:ref> <x:ref>SP</x:ref> <x:ref>time-of-day</x:ref> <x:ref>SP</x:ref> <x:ref>year</x:ref>
+  <x:ref>date3</x:ref>        = <x:ref>month</x:ref> <x:ref>SP</x:ref> ( 2<x:ref>DIGIT</x:ref> / ( <x:ref>SP</x:ref> 1<x:ref>DIGIT</x:ref> ))
+               ; e.g., Jun  2
+</sourcecode>
+<t>
+   HTTP-date is case sensitive. Note that <xref target="CACHING" x:fmt="of" x:rel="#expiration.model"/> relaxes this for cache recipients.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate additional whitespace in an HTTP-date beyond
+   that specifically included as SP in the grammar.
+   The semantics of <x:ref>day-name</x:ref>, <x:ref>day</x:ref>,
+   <x:ref>month</x:ref>, <x:ref>year</x:ref>, and <x:ref>time-of-day</x:ref>
+   are the same as those defined for the Internet Message Format constructs
+   with the corresponding name (<xref target="RFC5322" x:fmt="," x:sec="3.3"/>).
+</t>
+<t>
+   Recipients of a timestamp value in rfc850-date format, which uses a
+   two-digit year, <bcp14>MUST</bcp14> interpret a timestamp that appears to be more
+   than 50 years in the future as representing the most recent year in the
+   past that had the same last two digits.
+</t>
+<t>
+   Recipients of timestamp values are encouraged to be robust in parsing
+   timestamps unless otherwise restricted by the field definition.
+   For example, messages are occasionally forwarded over HTTP from a non-HTTP
+   source that might generate any of the date and time specifications defined
+   by the Internet Message Format.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> HTTP requirements for timestamp formats apply only
+    to their usage within the protocol stream. Implementations are
+    not required to use these formats for user presentation, request
+    logging, etc.
+  </t>
+</aside>
+</section>
+</section>
+</section>
+
+<section title="Message Abstraction" anchor="message.abstraction">
+<iref primary="true" item="message abstraction"/>
+<iref primary="true" item="message"/>
+<iref primary="true" item="self-descriptive"/>
+<t>
+   Each major version of HTTP defines its own syntax for communicating
+   messages. This section defines an abstract data type for HTTP messages
+   based on a generalization of those message characteristics, common structure,
+   and capacity for conveying semantics. This abstraction is used to define
+   requirements on senders and recipients that are independent of the HTTP
+   version, such that a message in one version can be relayed through other
+   versions without changing its meaning.
+</t>
+<t>
+   A <x:dfn>message</x:dfn> consists of the following:
+</t>
+<ul>
+  <li>control data to describe and route the message,</li>
+  <li>a headers lookup table of name/value pairs for extending that control
+      data and conveying additional information about the sender, message,
+      content, or context,</li>
+  <li>a potentially unbounded stream of content, and</li>
+  <li>a trailers lookup table of name/value pairs for communicating information
+      obtained while sending the content.</li>
+</ul>
+<t>
+   Framing and control data is sent first, followed by a header section
+   containing fields for the headers table. When a message includes content,
+   the content is sent after the header section, potentially followed by a
+   trailer section that might contain fields for the trailers table.
+</t>
+<t>
+   Messages are expected to be processed as a stream, wherein the purpose of
+   that stream and its continued processing is revealed while being read.
+   Hence, control data describes what the recipient needs to know immediately,
+   header fields describe what needs to be known before receiving content,
+   the content (when present) presumably contains what the recipient wants or
+   needs to fulfill the message semantics, and trailer fields provide
+   optional metadata that was unknown prior to sending the content.
+</t>
+<t>
+   Messages are intended to be <x:dfn>self-descriptive</x:dfn>:
+   everything a recipient needs to know about the message can be determined by
+   looking at the message itself, after decoding or reconstituting parts that
+   have been compressed or elided in transit, without requiring an
+   understanding of the sender's current application state (established via
+   prior messages). However, a client <bcp14>MUST</bcp14> retain knowledge of the request when
+   parsing, interpreting, or caching a corresponding response. For example,
+   responses to the <x:ref>HEAD</x:ref> method look just like the beginning of a
+   response to <x:ref>GET</x:ref> but cannot be parsed in the same manner.
+</t>
+<t>
+   Note that this message abstraction is a generalization across many versions
+   of HTTP, including features that might not be found in some versions. For
+   example, trailers were introduced within the HTTP/1.1 chunked transfer
+   coding as a trailer section after the content. An equivalent feature is
+   present in HTTP/2 and HTTP/3 within the header block that terminates each
+   stream.
+</t>
+
+<section title="Framing and Completeness" anchor="message.framing">
+<iref primary="true" item="complete"/>
+<iref primary="true" item="incomplete"/>
+<t>
+   Message framing indicates how each message begins and ends, such that each
+   message can be distinguished from other messages or noise on the same
+   connection. Each major version of HTTP defines its own framing mechanism.
+</t>
+<t>
+   HTTP/0.9 and early deployments of HTTP/1.0 used closure of the underlying
+   connection to end a response. For backwards compatibility, this implicit
+   framing is also allowed in HTTP/1.1. However, implicit framing can fail to
+   distinguish an incomplete response if the connection closes early. For
+   that reason, almost all modern implementations use explicit framing in
+   the form of length-delimited sequences of message data.
+</t>
+<t>
+   A message is considered <x:dfn>complete</x:dfn> when all of the octets
+   indicated by its framing are available. Note that,
+   when no explicit framing is used, a response message that is ended
+   by the underlying connection's close is considered complete even though it
+   might be indistinguishable from an incomplete response, unless a
+   transport-level error indicates that it is not complete.
+</t>
+</section>
+
+<section title="Control Data" anchor="message.control.data">
+<iref primary="true" item="control data"/>
+<t>
+   Messages start with control data that describe its primary purpose. Request
+   message control data includes a request method (<xref target="methods"/>),
+   request target (<xref target="target.resource"/>), and protocol version
+   (<xref target="protocol.version"/>). Response message control data includes
+   a status code (<xref target="status.codes"/>), optional reason phrase, and
+   protocol version.
+</t>
+<t>
+   In HTTP/1.1 (<xref target="HTTP11"/>) and earlier, control data is sent
+   as the first line of a message. In HTTP/2 (<xref target="HTTP2"/>) and
+   HTTP/3 (<xref target="HTTP3"/>), control data is sent as pseudo-header
+   fields with a reserved name prefix (e.g., ":authority").
+</t>
+<t>
+   Every HTTP message has a protocol version. Depending on the version in use,
+   it might be identified within the message explicitly or inferred by the
+   connection over which the message is received. Recipients use that version
+   information to determine limitations or potential for later communication
+   with that sender.
+</t>
+<t>
+   When a message is forwarded by an intermediary, the protocol version is
+   updated to reflect the version used by that intermediary.
+   The <x:ref>Via</x:ref> header field (<xref target="field.via"/>) is used to
+   communicate upstream protocol information within a forwarded message.
+</t>
+<t>
+   A client <bcp14>SHOULD</bcp14> send a request version equal to the highest
+   version to which the client is conformant and
+   whose major version is no higher than the highest version supported
+   by the server, if this is known.  A client <bcp14>MUST NOT</bcp14> send a
+   version to which it is not conformant.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> send a lower request version if it is known that
+   the server incorrectly implements the HTTP specification, but only
+   after the client has attempted at least one normal request and determined
+   from the response status code or header fields (e.g., <x:ref>Server</x:ref>) that
+   the server improperly handles higher request versions.
+</t>
+<t>
+   A server <bcp14>SHOULD</bcp14> send a response version equal to the highest version to
+   which the server is conformant that has a major version less than or equal
+   to the one received in the request.
+   A server <bcp14>MUST NOT</bcp14> send a version to which it is not conformant.
+   A server can send a <x:ref>505 (HTTP Version Not Supported)</x:ref>
+   response if it wishes, for any reason, to refuse service of the client's
+   major protocol version.
+</t>
+<t>
+   A recipient that receives a message with a major version number that it
+   implements and a minor version number higher than what it implements
+   <bcp14>SHOULD</bcp14> process the message as if it
+   were in the highest minor version within that major version to which the
+   recipient is conformant. A recipient can assume that a message with a
+   higher minor version, when sent to a recipient that has not yet indicated
+   support for that higher version, is sufficiently backwards-compatible to be
+   safely processed by any implementation of the same major version.
+</t>
+</section>
+
+<section title="Header Fields" anchor="header.fields">
+   <iref primary="true" item="header section"/>
+   <iref item="field"/>
+<t>
+   Fields (<xref target="fields"/>) that are sent or received before the content
+   are referred to as "header fields" (or just "headers", colloquially).
+</t>
+<t>
+   The <x:dfn>header section</x:dfn> of a message consists of a sequence of
+   header field lines. Each header field might modify or extend message
+   semantics, describe the sender, define the content, or provide additional
+   context.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> We refer to named fields specifically as a "header field" when they
+    are only allowed to be sent in the header section.
+  </t>
+</aside>
+</section>
+
+<section title="Content" anchor="content">
+<iref item="content"/>
+<t>
+   HTTP messages often transfer a complete or partial representation as the
+   message <x:dfn>content</x:dfn>: a stream of octets sent after the header
+   section, as delineated by the message framing.
+</t>
+<t>
+   This abstract definition of content reflects the data after it has been
+   extracted from the message framing. For example, an HTTP/1.1 message body
+   (<xref target="HTTP11" x:rel="#message.body"/>) might consist of a stream of data encoded
+   with the chunked transfer coding — a sequence of data chunks, one
+   zero-length chunk, and a trailer section — whereas
+   the content of that same message
+   includes only the data stream after the transfer coding has been decoded;
+   it does not include the chunk lengths, chunked framing syntax, nor the
+   trailer fields (<xref target="trailer.fields"/>).
+</t>
+<aside>
+   <t>
+      <x:h>Note:</x:h> Some field names have a "Content-" prefix. This is an informal
+      convention; while some of these fields refer to the content of the
+      message, as defined above, others are scoped to the selected representation
+      (<xref target="representations"/>). See the individual field's
+      definition to disambiguate.
+   </t>
+</aside>
+
+<section title="Content Semantics" anchor="content.semantics">
+<t>
+   The purpose of content in a request is defined by the method semantics
+   (<xref target="methods"/>).
+</t>
+<t>
+   For example, a representation in the content of a PUT request
+   (<xref target="PUT"/>) represents the desired state of the
+   <x:ref>target resource</x:ref> after the request is successfully applied,
+   whereas a representation in the content of a POST request
+   (<xref target="POST"/>) represents information to be processed by the
+   target resource.
+</t>
+<t>
+   In a response, the content's purpose is defined by the request method,
+   response status code (<xref target="status.codes"/>), and response
+   fields describing that content.
+   For example, the content of a <x:ref>200 (OK)</x:ref> response to GET
+   (<xref target="GET"/>) represents the current state of the
+   <x:ref>target resource</x:ref>, as observed at the time of the message
+   origination date (<xref target="field.date"/>), whereas the content of
+   the same status code in a response to POST might represent either the
+   processing result or the new state of the target resource after applying
+   the processing.
+</t>
+<t>
+   The content of a <x:ref>206 (Partial Content)</x:ref> response to GET
+   contains either a single part of the selected representation or a
+   multipart message body containing multiple parts of that representation,
+   as described in <xref target="status.206"/>.
+</t>
+<t>
+   Response messages with an error status code usually contain content that
+   represents the error condition, such that the content describes the
+   error state and what steps are suggested for resolving it.
+</t>
+<t>
+   Responses to the HEAD request method (<xref target="HEAD"/>) never include
+   content; the associated response header fields indicate only
+   what their values would have been if the request method had been GET
+   (<xref target="GET"/>).
+</t>
+<t>
+   <x:ref>2xx (Successful)</x:ref> responses to a CONNECT request method
+   (<xref target="CONNECT"/>) switch the connection to tunnel mode instead of
+   having content.
+</t>
+<t>
+   All <x:ref>1xx (Informational)</x:ref>, <x:ref>204 (No Content)</x:ref>, and
+   <x:ref>304 (Not Modified)</x:ref> responses do not include content.
+</t>
+<t>
+   All other responses do include content, although that content
+   might be of zero length.
+</t>
+</section>
+
+<section title="Identifying Content" anchor="identifying.content">
+<t>
+   When a complete or partial representation is transferred as message
+   content, it is often desirable for the sender to supply, or the recipient
+   to determine, an identifier for a resource corresponding to that specific
+   representation. For example, a client making a GET request on a resource
+   for "the current weather report" might want an identifier specific to the
+   content returned (e.g., "weather report for Laguna Beach at 20210720T1711").
+   This can be useful for sharing or bookmarking content from resources that
+   are expected to have changing representations over time.
+</t>
+<t>
+   For a request message:
+</t>
+<ul>
+   <li>If the request has a <x:ref>Content-Location</x:ref> header field,
+       then the sender asserts that the content is a representation of the
+       resource identified by the Content-Location field value. However,
+       such an assertion cannot be trusted unless it can be verified by
+       other means (not defined by this specification). The information
+       might still be useful for revision history links.</li>
+   <li>Otherwise, the content is unidentified by HTTP, but a more specific
+       identifier might be supplied within the content itself.</li>
+</ul>
+<t>
+   For a response message, the following rules are applied in order until a
+   match is found:
+</t>
+<ol>
+   <li>If the request method is HEAD or the response status code is
+       <x:ref>204 (No Content)</x:ref> or <x:ref>304 (Not Modified)</x:ref>,
+       there is no content in the response.</li>
+   <li>If the request method is GET and the response status code is
+       <x:ref>200 (OK)</x:ref>,
+       the content is a representation of the <x:ref>target resource</x:ref> (<xref target="target.resource"/>).</li>
+   <li>If the request method is GET and the response status code is
+       <x:ref>203 (Non-Authoritative Information)</x:ref>, the content is
+       a potentially modified or enhanced representation of the
+       <x:ref>target resource</x:ref> as provided by an intermediary.</li>
+   <li>If the request method is GET and the response status code is
+       <x:ref>206 (Partial Content)</x:ref>,
+       the content is one or more parts of a representation of the
+       target resource.</li>
+   <li>If the response has a <x:ref>Content-Location</x:ref> header field
+       and its field value is a reference to the same URI as the target URI,
+       the content is a representation of the target resource.</li>
+   <li>If the response has a <x:ref>Content-Location</x:ref> header field
+       and its field value is a reference to a URI different from the
+       target URI, then the sender asserts that the content is a
+       representation of the resource identified by the Content-Location
+       field value. However, such an assertion cannot be trusted unless
+       it can be verified by other means (not defined by this specification).</li>
+   <li>Otherwise, the content is unidentified by HTTP, but a more specific
+       identifier might be supplied within the content itself.</li>
+</ol>
+</section>
+</section>
+
+<section title="Trailer Fields" anchor="trailer.fields">
+<iref primary="true" item="trailer section"/>
+<iref primary="true" item="Trailer Fields"/>
+<iref primary="true" item="trailers"/>
+<t>
+   Fields (<xref target="fields"/>) that are located within a
+   <x:dfn>trailer section</x:dfn> are referred to as "trailer fields"
+   (or just "trailers", colloquially).
+   Trailer fields can be useful for supplying message integrity checks, digital
+   signatures, delivery metrics, or post-processing status information.
+</t>
+<t>
+   Trailer fields ought to be processed and stored separately from the fields
+   in the header section to avoid contradicting message semantics known at
+   the time the header section was complete. The presence or absence of
+   certain header fields might impact choices made for the routing or
+   processing of the message as a whole before the trailers are received;
+   those choices cannot be unmade by the later discovery of trailer fields.
+</t>
+
+<section title="Limitations on Use of Trailers" anchor="trailers.limitations">
+<t>
+   A trailer section is only possible when supported by the version
+   of HTTP in use and enabled by an explicit framing mechanism.
+   For example, the chunked transfer coding in HTTP/1.1 allows a trailer section to be
+   sent after the content (<xref target="HTTP11" x:rel="#chunked.trailer.section"/>).
+ </t>
+<t>
+   Many fields cannot be processed outside the header section because
+   their evaluation is necessary prior to receiving the content, such as
+   those that describe message framing, routing, authentication,
+   request modifiers, response controls, or content format.
+   A sender <bcp14>MUST NOT</bcp14> generate a trailer field unless the sender knows the
+   corresponding header field name's definition permits the field to be sent
+   in trailers.
+</t>
+<t>
+   Trailer fields can be difficult to process by intermediaries that forward
+   messages from one protocol version to another. If the entire message can be
+   buffered in transit, some intermediaries could merge trailer fields into
+   the header section (as appropriate) before it is forwarded. However, in
+   most cases, the trailers are simply discarded.
+   A recipient <bcp14>MUST NOT</bcp14> merge a trailer field into a header section unless
+   the recipient understands the corresponding header field definition and
+   that definition explicitly permits and defines how trailer field values
+   can be safely merged.
+</t>
+<t>
+   The presence of the keyword "trailers" in the TE header field (<xref target="field.te"/>) of a request indicates that the client is willing to
+   accept trailer fields, on behalf of itself and any downstream clients. For
+   requests from an intermediary, this implies that all
+   downstream clients are willing to accept trailer fields in the forwarded
+   response. Note that the presence of "trailers" does not mean that the
+   client(s) will process any particular trailer field in the response; only
+   that the trailer section(s) will not be dropped by any of the clients.
+</t>
+<t>
+   Because of the potential for trailer fields to be discarded in transit, a
+   server <bcp14>SHOULD NOT</bcp14> generate trailer fields that it believes are necessary
+   for the user agent to receive.
+</t>
+</section>
+
+<section title="Processing Trailer Fields" anchor="trailers.processing">
+<t>
+   The "Trailer" header field (<xref target="field.trailer"/>) can be sent
+   to indicate fields likely to be sent in the trailer section, which allows
+   recipients to prepare for their receipt before processing the content.
+   For example, this could be useful if a field name indicates that a dynamic
+   checksum should be calculated as the content is received and then
+   immediately checked upon receipt of the trailer field value.
+</t>
+<t>
+   Like header fields, trailer fields with the same name are processed in the
+   order received; multiple trailer field lines with the same name have the
+   equivalent semantics as appending the multiple values as a list of members.
+   Trailer fields that might be generated more than once during a message
+   <bcp14>MUST</bcp14> be defined as a list-based field even if each member value is only
+   processed once per field line received.
+</t>
+<t>
+   At the end of a message, a recipient <bcp14>MAY</bcp14> treat the set of received
+   trailer fields as a data structure of name/value pairs, similar to (but
+   separate from) the header fields. Additional processing expectations, if
+   any, can be defined within the field specification for a field intended
+   for use in trailers.
+</t>
+</section>
+</section>
+
+<section title="Message Metadata" anchor="message.metadata">
+<t>
+   Fields that describe the message itself, such as when and how the
+   message has been generated, can appear in both requests and responses.
+</t>
+
+<section title="Date" anchor="field.date">
+  <x:anchor-alias value="header.date"/>
+  <iref primary="true" item="Fields" subitem="Date" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Date" x:for-anchor=""/><iref primary="true" item="Date header field" x:for-anchor=""/>
+  <x:anchor-alias value="Date"/>
+<t>
+   The "Date" header field represents the date and time at which
+   the message was originated, having the same semantics as the Origination
+   Date Field (orig-date) defined in <xref target="RFC5322" x:fmt="of" x:sec="3.6.1"/>.
+   The field value is an HTTP-date, as defined in <xref target="http.date"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Date"/>
+  <x:ref>Date</x:ref> = <x:ref>HTTP-date</x:ref>
+</sourcecode>
+<t>
+   An example is
+</t>
+<sourcecode type="http-message">
+Date: Tue, 15 Nov 1994 08:12:31 GMT
+</sourcecode>
+<t>
+   A sender that generates a Date header field <bcp14>SHOULD</bcp14> generate its
+   field value as the best available approximation of the date and time of
+   message generation. In theory, the date ought to represent the moment just
+   before generating the message content. In practice, a sender can generate
+   the date value at any time during message origination.
+</t>
+<t>
+   An origin server with a clock (as defined in
+   <xref target="http.date"/>) <bcp14>MUST</bcp14> generate a Date header field in
+   all <x:ref>2xx (Successful)</x:ref>, <x:ref>3xx (Redirection)</x:ref>,
+   and <x:ref>4xx (Client Error)</x:ref> responses,
+   and <bcp14>MAY</bcp14> generate a Date header field in
+   <x:ref>1xx (Informational)</x:ref> and
+   <x:ref>5xx (Server Error)</x:ref> responses.
+</t>
+<t>
+   An origin server without a clock <bcp14>MUST NOT</bcp14> generate a Date header field.
+</t>
+<t>
+   A recipient with a clock that receives a response message without a Date
+   header field <bcp14>MUST</bcp14> record the time it was received and append a
+   corresponding Date header field to the message's header section if it is
+   cached or forwarded downstream.
+</t>
+<t>
+   A recipient with a clock that receives a response with an invalid Date
+   header field value <bcp14>MAY</bcp14> replace that value with the time that
+   response was received.
+</t>
+<t>
+   A user agent <bcp14>MAY</bcp14> send a Date header field in a request, though generally
+   will not do so unless it is believed to convey useful information to the
+   server. For example, custom applications of HTTP might convey a Date if
+   the server is expected to adjust its interpretation of the user's request
+   based on differences between the user agent and server clocks.
+</t>
+</section>
+
+<section title="Trailer" anchor="field.trailer">
+  <x:anchor-alias value="header.trailer"/>
+  <iref primary="true" item="Fields" subitem="Trailer" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Trailer" x:for-anchor=""/><iref primary="true" item="Trailer header field" x:for-anchor=""/>
+  <x:anchor-alias value="Trailer"/>
+<t>
+   The "Trailer" header field provides a list of field names that the sender
+   anticipates sending as trailer fields within that message. This allows a
+   recipient to prepare for receipt of the indicated metadata before it starts
+   processing the content.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Trailer"/><iref primary="false" item="Grammar" subitem="field-name"/>
+  <x:ref>Trailer</x:ref> = #<x:ref>field-name</x:ref>
+</sourcecode>
+<t>
+   For example, a sender might indicate that a signature will
+   be computed as the content is being streamed and provide the final
+   signature as a trailer field. This allows a recipient to perform the same
+   check on the fly as it receives the content.
+</t>
+<t>
+   A sender that intends to generate one or more trailer fields in a message
+   <bcp14>SHOULD</bcp14> generate a <x:ref>Trailer</x:ref> header field in the header
+   section of that message to indicate which fields might be present in the
+   trailers.
+</t>
+<t>
+   If an intermediary discards the trailer section in transit, the
+   <x:ref>Trailer</x:ref> field could provide a hint of what metadata
+   was lost, though there is no guarantee that a sender of Trailer
+   will always follow through by sending the named fields.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Routing HTTP Messages" anchor="routing">
+<t>
+   HTTP request message routing is determined by each client based on the
+   target resource, the client's proxy configuration, and
+   establishment or reuse of an inbound connection.  The corresponding
+   response routing follows the same connection chain back to the client.
+</t>
+
+<section title="Determining the Target Resource" anchor="target.resource">
+  <iref primary="true" item="target resource"/>
+  <iref primary="true" item="target URI"/>
+  <iref primary="true" item="request target"/>
+  <x:anchor-alias value="target resource"/>
+  <x:anchor-alias value="target URI"/>
+  <x:anchor-alias value="request target"/>
+  <x:anchor-alias value="request.target"/>
+  <x:anchor-alias value="reconstructing.target.uri"/>
+<t>
+   Although HTTP is used in a wide variety of applications, most clients rely
+   on the same resource identification mechanism and configuration techniques
+   as general-purpose Web browsers. Even when communication options are
+   hard-coded in a client's configuration, we can think of their combined
+   effect as a URI reference (<xref target="uri.references"/>).
+</t>
+<t>
+   A URI reference is resolved to its absolute form in order to obtain the
+   <x:dfn>target URI</x:dfn>. The target URI excludes the reference's
+   fragment component, if any, since fragment identifiers are reserved for
+   client-side processing (<xref target="URI" x:fmt="," x:sec="3.5"/>).
+</t>
+<t>
+   To perform an action on a <x:dfn>target resource</x:dfn>, the client sends
+   a request message containing enough components of its parsed target URI to
+   enable recipients to identify that same resource. For historical reasons,
+   the parsed target URI components, collectively referred to as the
+   <x:dfn>request target</x:dfn>, are sent within the message control data
+   and the <x:ref>Host</x:ref> header field (<xref target="field.host"/>).
+</t>
+<t>
+   There are two unusual cases for which the request target components are in
+   a method-specific form:
+ </t>
+ <ul>
+   <li>
+      For CONNECT (<xref target="CONNECT"/>), the request target is the host
+      name and port number of the tunnel destination, separated by a colon.
+   </li>
+   <li>
+      For OPTIONS (<xref target="OPTIONS"/>), the request target can be a
+      single asterisk ("*").
+   </li>
+</ul>
+<t>
+   See the respective method definitions for details. These forms <bcp14>MUST NOT</bcp14>
+   be used with other methods.
+</t>
+<t>
+   Upon receipt of a client's request, a server reconstructs the target URI
+   from the received components in accordance with their local configuration
+   and incoming connection context. This reconstruction is specific to each
+   major protocol version. For example,
+   <xref target="HTTP11" x:rel="#h1.effective.request.uri"/> defines how a server
+   determines the target URI of an HTTP/1.1 request.
+</t>
+<aside anchor="effective.request.uri">
+  <t>
+    <iref primary="true" item="effective request URI"/>
+    <x:h>Note:</x:h> Previous specifications defined the recomposed target URI as a
+    distinct concept, the <x:dfn>effective request URI</x:dfn>.
+  </t>
+</aside>
+</section>
+
+<section title="Host and :authority" anchor="field.host">
+  <x:anchor-alias value="header.host"/>
+  <iref primary="true" item="Fields" subitem="Host" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Host" x:for-anchor=""/><iref primary="true" item="Host header field" x:for-anchor=""/>
+  <x:anchor-alias value="Host"/>
+  <x:anchor-alias value="pseudo-authority"/>
+<t>
+   The "Host" header field in a request provides the host and port
+   information from the target URI, enabling the origin
+   server to distinguish among resources while servicing requests
+   for multiple host names.
+</t>
+<t>
+   In HTTP/2 <xref target="HTTP2"/> and HTTP/3 <xref target="HTTP3"/>, the
+   Host header field is, in some cases, supplanted by the ":authority"
+   pseudo-header field of a request's control data.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Host"/>
+  <x:ref>Host</x:ref> = <x:ref>uri-host</x:ref> [ ":" <x:ref>port</x:ref> ] ; <xref target="uri"/>
+</sourcecode>
+<t>
+   The target URI's authority information is critical for handling a
+   request. A user agent <bcp14>MUST</bcp14> generate a Host header field in a request
+   unless it sends that information as an ":authority" pseudo-header field.
+   A user agent that sends Host <bcp14>SHOULD</bcp14> send it as the first field in the
+   header section of a request.
+</t>
+<t>
+   For example, a GET request to the origin server for
+   &lt;http://www.example.org/pub/WWW/&gt; would begin with:
+</t>
+<sourcecode type="http-message">
+GET /pub/WWW/ HTTP/1.1
+Host: www.example.org
+</sourcecode>
+<t>
+   Since the host and port information acts as an application-level routing
+   mechanism, it is a frequent target for malware seeking to poison
+   a shared cache or redirect a request to an unintended server.
+   An interception proxy is particularly vulnerable if it relies on
+   the host and port information for redirecting requests to internal
+   servers, or for use as a cache key in a shared cache, without
+   first verifying that the intercepted connection is targeting a
+   valid IP address for that host.
+</t>
+</section>
+
+<section title="Routing Inbound Requests" anchor="routing.inbound">
+<t>
+   Once the target URI and its origin are determined, a client decides whether
+   a network request is necessary to accomplish the desired semantics and,
+   if so, where that request is to be directed.
+</t>
+
+<section title="To a Cache" anchor="routing.cache">
+<t>
+   If the client has a cache <xref target="CACHING"/> and the request can be
+   satisfied by it, then the request is
+   usually directed there first.
+</t>
+</section>
+
+<section title="To a Proxy" anchor="routing.proxy">
+<t>
+   If the request is not satisfied by a cache, then a typical client will
+   check its configuration to determine whether a proxy is to be used to
+   satisfy the request.  Proxy configuration is implementation-dependent,
+   but is often based on URI prefix matching, selective authority matching,
+   or both, and the proxy itself is usually identified by an "http" or
+   "https" URI.
+</t>
+<t>
+   If an "http" or "https" proxy is applicable, the client connects
+   inbound by establishing (or reusing) a connection to that proxy and
+   then sending it an HTTP request message containing a request target
+   that matches the client's target URI.
+</t>
+</section>
+
+<section title="To the Origin" anchor="routing.origin">
+<t>
+   If no proxy is applicable, a typical client will invoke a handler
+   routine (specific to the target URI's scheme) to obtain access to the
+   identified resource. How that is accomplished is dependent on the
+   target URI scheme and defined by its associated specification.
+</t>
+<t>
+   <xref target="http.origin"/> defines how to obtain access to an
+   "http" resource by establishing (or reusing) an inbound connection to
+   the identified origin server and then sending it an HTTP request message
+   containing a request target that matches the client's target URI.
+</t>
+<t>
+   <xref target="https.origin"/> defines how to obtain access to an
+   "https" resource by establishing (or reusing) an inbound secured
+   connection to an origin server that is authoritative for the identified
+   origin and then sending it an HTTP request message containing a request
+   target that matches the client's target URI.
+</t>
+</section>
+</section>
+
+<section title="Rejecting Misdirected Requests" anchor="routing.reject">
+<t>
+   Once a request is received by a server and parsed sufficiently to determine
+   its target URI, the server decides whether to process the request itself,
+   forward the request to another server, redirect the client to a different
+   resource, respond with an error, or drop the connection. This decision can
+   be influenced by anything about the request or connection context, but is
+   specifically directed at whether the server has been configured to process
+   requests for that target URI and whether the connection context is
+   appropriate for that request.
+</t>
+<t>
+   For example, a request might have been misdirected,
+   deliberately or accidentally, such that the information within a received
+   <x:ref>Host</x:ref> header field differs from the connection's host or port.
+   If the connection is from a trusted gateway, such inconsistency might
+   be expected; otherwise, it might indicate an attempt to bypass security
+   filters, trick the server into delivering non-public content, or poison a
+   cache. See <xref target="security.considerations"/> for security
+   considerations regarding message routing.
+</t>
+<t>
+   Unless the connection is from a trusted gateway,
+   an origin server <bcp14>MUST</bcp14> reject a request if any scheme-specific requirements
+   for the target URI are not met. In particular,
+   a request for an "https" resource <bcp14>MUST</bcp14> be rejected unless it has been
+   received over a connection that has been secured via a certificate
+   valid for that target URI's origin, as defined by <xref target="https.uri"/>.
+</t>
+<t>
+   The <x:ref>421 (Misdirected Request)</x:ref> status code in a response
+   indicates that the origin server has rejected the request because it
+   appears to have been misdirected (<xref target="status.421"/>).
+</t>
+</section>
+
+<section title="Response Correlation" anchor="response.correlation">
+<t>
+   A connection might be used for multiple request/response exchanges. The
+   mechanism used to correlate between request and response messages is
+   version dependent; some versions of HTTP use implicit ordering of
+   messages, while others use an explicit identifier.
+</t>
+<t>
+   All responses, regardless of the status code (including <x:ref>interim</x:ref>
+   responses) can be sent at any time after a request is received, even if the
+   request is not yet complete. A response can complete before its
+   corresponding request is complete (<xref target="message.framing"/>). Likewise, clients are not expected
+   to wait any specific amount of time for a response. Clients
+   (including intermediaries) might abandon a request if the response is not
+   received within a reasonable period of time.
+</t>
+<t>
+   A client that receives a response while it is still sending the associated
+   request <bcp14>SHOULD</bcp14> continue sending that request unless it receives
+   an explicit indication to the contrary (see, e.g., <xref target="HTTP11" x:rel="#persistent.failures"/> and <xref target="HTTP2" section="6.4"/>).
+</t>
+</section>
+
+<section title="Message Forwarding" anchor="message.forwarding">
+<t>
+   As described in <xref target="intermediaries"/>, intermediaries can serve
+   a variety of roles in the processing of HTTP requests and responses.
+   Some intermediaries are used to improve performance or availability.
+   Others are used for access control or to filter content.
+   Since an HTTP stream has characteristics similar to a pipe-and-filter
+   architecture, there are no inherent limits to the extent an intermediary
+   can enhance (or interfere) with either direction of the stream.
+</t>
+<t>
+   Intermediaries are expected to forward messages even when protocol elements
+   are not recognized (e.g., new methods, status codes, or field names) since that
+   preserves extensibility for downstream recipients.
+</t>
+<t>
+   An intermediary not acting as a tunnel <bcp14>MUST</bcp14> implement the
+   <x:ref>Connection</x:ref> header field, as specified in
+   <xref target="field.connection"/>, and exclude fields from being forwarded
+   that are only intended for the incoming connection.
+</t>
+<t>
+   An intermediary <bcp14>MUST NOT</bcp14> forward a message to itself unless it is
+   protected from an infinite request loop. In general, an intermediary ought
+   to recognize its own server names, including any aliases, local variations,
+   or literal IP addresses, and respond to such requests directly.
+</t>
+<t>
+   An HTTP message can be parsed as a stream for incremental processing or
+   forwarding downstream.
+   However, senders and recipients cannot rely on incremental
+   delivery of partial messages, since some implementations will buffer or
+   delay message forwarding for the sake of network efficiency, security
+   checks, or content transformations.
+</t>
+
+<section title="Connection" anchor="field.connection">
+  <x:anchor-alias value="header.connection"/>
+  <iref primary="true" item="Fields" subitem="Connection" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Connection" x:for-anchor=""/><iref primary="true" item="Connection header field" x:for-anchor=""/>
+  <x:anchor-alias value="Connection"/>
+  <x:anchor-alias value="connection-option"/>
+<t>
+   The "Connection" header field allows the sender to list desired
+   control options for the current connection.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Connection"/><iref primary="true" item="Grammar" subitem="connection-option"/>
+  <x:ref>Connection</x:ref>        = #<x:ref>connection-option</x:ref>
+  <x:ref>connection-option</x:ref> = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   Connection options are case-insensitive.
+</t>
+<t>
+   When a field aside from Connection is used to supply control
+   information for or about the current connection, the sender <bcp14>MUST</bcp14> list
+   the corresponding field name within the Connection header field.
+   Note that some versions of HTTP prohibit the use of fields for such
+   information, and therefore do not allow the Connection field.
+</t>
+<t>
+   Intermediaries <bcp14>MUST</bcp14> parse a received Connection
+   header field before a message is forwarded and, for each
+   connection-option in this field, remove any header or trailer field(s) from
+   the message with the same name as the connection-option, and then
+   remove the Connection header field itself (or replace it with the
+   intermediary's own control options for the forwarded message).
+</t>
+<t>
+   Hence, the Connection header field provides a declarative way of
+   distinguishing fields that are only intended for the
+   immediate recipient ("hop-by-hop") from those fields that are
+   intended for all recipients on the chain ("end-to-end"), enabling the
+   message to be self-descriptive and allowing future connection-specific
+   extensions to be deployed without fear that they will be blindly
+   forwarded by older intermediaries.
+</t>
+<t>
+   Furthermore, intermediaries <bcp14>SHOULD</bcp14> remove or replace fields
+   that are known to require removal before forwarding, whether or not
+   they appear as a connection-option, after applying those fields'
+   semantics. This includes but is not limited to:
+</t>
+<ul>
+   <li>Proxy-Connection (<xref target="HTTP11" x:rel="#compatibility.with.http.1.0.persistent.connections"/>)</li>
+   <li>Keep-Alive (<xref x:sec="19.7.1" x:fmt="of" target="RFC2068"/>)</li>
+   <li>TE (<xref target="field.te"/>)</li>
+   <li>Transfer-Encoding (<xref target="HTTP11" x:rel="#field.transfer-encoding"/>)</li>
+   <li>Upgrade (<xref target="field.upgrade"/>)</li>
+</ul>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> send a connection option corresponding to a
+   field that is intended for all recipients of the content.
+   For example, <x:ref>Cache-Control</x:ref> is never appropriate as a
+   connection option (<xref target="CACHING" x:rel="#field.cache-control"/>).
+</t>
+<t>
+   Connection options do not always correspond to a field
+   present in the message, since a connection-specific field
+   might not be needed if there are no parameters associated with a
+   connection option. In contrast, a connection-specific field
+   received without a corresponding connection option usually indicates
+   that the field has been improperly forwarded by an intermediary and
+   ought to be ignored by the recipient.
+</t>
+<t>
+   When defining a new connection option that does not correspond to a field,
+   specification authors ought to reserve the corresponding field name
+   anyway in order to avoid later collisions. Such reserved field names are
+   registered in the "Hypertext Transfer Protocol (HTTP) Field Name Registry"
+   (<xref target="fields.registry"/>).
+</t>
+</section>
+
+<section title="Max-Forwards" anchor="field.max-forwards">
+  <x:anchor-alias value="header.max-forwards"/>
+  <iref primary="true" item="Fields" subitem="Max-Forwards" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Max-Forwards" x:for-anchor=""/><iref primary="true" item="Max-Forwards header field" x:for-anchor=""/>
+  <x:anchor-alias value="Max-Forwards"/>
+<t>
+   The "Max-Forwards" header field provides a mechanism with the
+   TRACE (<xref target="TRACE"/>) and OPTIONS (<xref target="OPTIONS"/>)
+   request methods to limit the number of times that the request is forwarded by
+   proxies. This can be useful when the client is attempting to
+   trace a request that appears to be failing or looping mid-chain.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Max-Forwards"/>
+  <x:ref>Max-Forwards</x:ref> = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   The Max-Forwards value is a decimal integer indicating the remaining
+   number of times this request message can be forwarded.
+</t>
+<t>
+   Each intermediary that receives a TRACE or OPTIONS request containing a
+   Max-Forwards header field <bcp14>MUST</bcp14> check and update its value prior to
+   forwarding the request. If the received value is zero (0), the intermediary
+   <bcp14>MUST NOT</bcp14> forward the request; instead, the intermediary <bcp14>MUST</bcp14> respond as
+   the final recipient. If the received Max-Forwards value is greater than
+   zero, the intermediary <bcp14>MUST</bcp14> generate an updated Max-Forwards field in the
+   forwarded message with a field value that is the lesser of a) the received
+   value decremented by one (1) or b) the recipient's maximum supported value
+   for Max-Forwards.
+</t>
+<t>
+   A recipient <bcp14>MAY</bcp14> ignore a Max-Forwards header field received with any
+   other request methods.
+</t>
+</section>
+
+<section title="Via" anchor="field.via">
+  <x:anchor-alias value="header.via"/>
+  <iref primary="true" item="Fields" subitem="Via" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Via" x:for-anchor=""/><iref primary="true" item="Via header field" x:for-anchor=""/>
+  <x:anchor-alias value="pseudonym"/>
+  <x:anchor-alias value="received-by"/>
+  <x:anchor-alias value="received-protocol"/>
+  <x:anchor-alias value="Via"/>
+<t>
+   The "Via" header field indicates the presence of intermediate protocols and
+   recipients between the user agent and the server (on requests) or between
+   the origin server and the client (on responses), similar to the
+   "Received" header field in email
+   (<xref target="RFC5322" x:fmt="of" x:sec="3.6.7"/>).
+   Via can be used for tracking message forwards,
+   avoiding request loops, and identifying the protocol capabilities of
+   senders along the request/response chain.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Via"/><iref primary="true" item="Grammar" subitem="received-protocol"/><iref primary="true" item="Grammar" subitem="protocol-name"/><iref primary="true" item="Grammar" subitem="protocol-version"/><iref primary="true" item="Grammar" subitem="received-by"/><iref primary="true" item="Grammar" subitem="pseudonym"/>
+  <x:ref>Via</x:ref> = #( <x:ref>received-protocol</x:ref> <x:ref>RWS</x:ref> <x:ref>received-by</x:ref> [ <x:ref>RWS</x:ref> <x:ref>comment</x:ref> ] )
+
+  <x:ref>received-protocol</x:ref> = [ <x:ref>protocol-name</x:ref> "/" ] <x:ref>protocol-version</x:ref>
+                    ; see <xref target="field.upgrade"/>
+  <x:ref>received-by</x:ref>       = <x:ref>pseudonym</x:ref> [ ":" <x:ref>port</x:ref> ]
+  <x:ref>pseudonym</x:ref>         = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   Each member of the Via field value represents a proxy or gateway that has
+   forwarded the message. Each intermediary appends its own information
+   about how the message was received, such that the end result is ordered
+   according to the sequence of forwarding recipients.
+</t>
+<t>
+   A proxy <bcp14>MUST</bcp14> send an appropriate Via header field, as described below, in
+   each message that it forwards.
+   An HTTP-to-HTTP gateway <bcp14>MUST</bcp14> send an appropriate Via header field in
+   each inbound request message and <bcp14>MAY</bcp14> send a Via header field in
+   forwarded response messages.
+</t>
+<t>
+   For each intermediary, the received-protocol indicates the protocol and
+   protocol version used by the upstream sender of the message. Hence, the
+   Via field value records the advertised protocol capabilities of the
+   request/response chain such that they remain visible to downstream
+   recipients; this can be useful for determining what backwards-incompatible
+   features might be safe to use in response, or within a later request, as
+   described in <xref target="protocol.version"/>. For brevity, the protocol-name
+   is omitted when the received protocol is HTTP.
+</t>
+<t>
+   The received-by portion is normally the host and optional
+   port number of a recipient server or client that subsequently forwarded the
+   message.
+   However, if the real host is considered to be sensitive information, a
+   sender <bcp14>MAY</bcp14> replace it with a pseudonym. If a port is not provided,
+   a recipient <bcp14>MAY</bcp14> interpret that as meaning it was received on the default
+   port, if any, for the received-protocol.
+</t>
+<t>
+   A sender <bcp14>MAY</bcp14> generate comments to identify the
+   software of each recipient, analogous to the <x:ref>User-Agent</x:ref> and
+   <x:ref>Server</x:ref> header fields. However, comments in Via
+   are optional, and a recipient <bcp14>MAY</bcp14> remove them prior to forwarding the
+   message.
+</t>
+<t>
+   For example, a request message could be sent from an HTTP/1.0 user
+   agent to an internal proxy code-named "fred", which uses HTTP/1.1 to
+   forward the request to a public proxy at p.example.net, which completes
+   the request by forwarding it to the origin server at www.example.com.
+   The request received by www.example.com would then have the following
+   Via header field:
+</t>
+<sourcecode type="http-message">
+Via: 1.0 fred, 1.1 p.example.net
+</sourcecode>
+<t>
+   An intermediary used as a portal through a network firewall
+   <bcp14>SHOULD NOT</bcp14> forward the names and ports of hosts within the firewall
+   region unless it is explicitly enabled to do so. If not enabled, such an
+   intermediary <bcp14>SHOULD</bcp14> replace each received-by host of any host behind the
+   firewall by an appropriate pseudonym for that host.
+</t>
+<t>
+   An intermediary <bcp14>MAY</bcp14> combine an ordered subsequence of Via header
+   field list members into a single member if the entries have identical
+   received-protocol values. For example,
+</t>
+<sourcecode type="http-message">
+Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
+</sourcecode>
+<t>
+  could be collapsed to
+</t>
+<sourcecode type="http-message">
+Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
+</sourcecode>
+<t>
+   A sender <bcp14>SHOULD NOT</bcp14> combine multiple list members unless they are all
+   under the same organizational control and the hosts have already been
+   replaced by pseudonyms. A sender <bcp14>MUST NOT</bcp14> combine members that
+   have different received-protocol values.
+</t>
+</section>
+</section>
+
+<section title="Message Transformations" anchor="message.transformations">
+   <iref primary="true" item="transforming proxy"/>
+   <iref primary="true" item="non-transforming proxy"/>
+<t>
+   Some intermediaries include features for transforming messages and their
+   content. A proxy might, for example, convert between image formats in
+   order to save cache space or to reduce the amount of traffic on a slow
+   link. However, operational problems might occur when these transformations
+   are applied to content intended for critical applications, such as medical
+   imaging or scientific data analysis, particularly when integrity checks or
+   digital signatures are used to ensure that the content received is
+   identical to the original.
+</t>
+<t>
+   An HTTP-to-HTTP proxy is called a <x:dfn>transforming proxy</x:dfn>
+   if it is designed or configured to modify messages in a semantically
+   meaningful way (i.e., modifications, beyond those required by normal
+   HTTP processing, that change the message in a way that would be
+   significant to the original sender or potentially significant to
+   downstream recipients).  For example, a transforming proxy might be
+   acting as a shared annotation server (modifying responses to include
+   references to a local annotation database), a malware filter, a
+   format transcoder, or a privacy filter. Such transformations are presumed
+   to be desired by whichever client (or client organization) chose the
+   proxy.
+</t>
+<t>
+   If a proxy receives a target URI with a host name that is not a
+   fully qualified domain name, it <bcp14>MAY</bcp14> add its own domain to the host name
+   it received when forwarding the request.  A proxy <bcp14>MUST NOT</bcp14> change the
+   host name if the target URI contains a fully qualified domain name.
+</t>
+<t>
+   A proxy <bcp14>MUST NOT</bcp14> modify the "absolute-path" and "query" parts of the
+   received target URI when forwarding it to the next inbound server except
+   as required by that forwarding protocol. For example, a proxy forwarding
+   a request to an origin server via HTTP/1.1 will replace an empty path with
+   "/" (<xref target="HTTP11" x:rel="#origin-form"/>) or "*" (<xref target="HTTP11" x:rel="#asterisk-form"/>),
+   depending on the request method.
+</t>
+<t>
+   A proxy <bcp14>MUST NOT</bcp14> transform the content (<xref target="content"/>) of a
+   response message that contains a no-transform cache directive
+   (<xref target="CACHING" x:rel="#cache-response-directive.no-transform"/>). Note that this
+   does not apply to message transformations that do not change the content,
+   such as the addition or removal of transfer codings
+   (<xref target="HTTP11" x:rel="#transfer.codings"/>).
+</t>
+<t>
+   A proxy <bcp14>MAY</bcp14> transform the content of a message
+   that does not contain a no-transform cache directive.
+   A proxy that transforms the content of a <x:ref>200 (OK)</x:ref> response
+   can inform downstream recipients that a transformation has been
+   applied by changing the response status code to
+   <x:ref>203 (Non-Authoritative Information)</x:ref> (<xref target="status.203"/>).
+</t>
+<t>
+   A proxy <bcp14>SHOULD NOT</bcp14> modify header fields that provide information about
+   the endpoints of the communication chain, the resource state, or the
+   <x:ref>selected representation</x:ref> (other than the content) unless the field's
+   definition specifically allows such modification or the modification is
+   deemed necessary for privacy or security.
+</t>
+</section>
+
+<section title="Upgrade" anchor="field.upgrade">
+  <x:anchor-alias value="header.upgrade"/>
+  <iref primary="true" item="Fields" subitem="Upgrade" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Upgrade" x:for-anchor=""/><iref primary="true" item="Upgrade header field" x:for-anchor=""/>
+  <x:anchor-alias value="Upgrade"/>
+  <x:anchor-alias value="protocol"/>
+  <x:anchor-alias value="protocol-name"/>
+  <x:anchor-alias value="protocol-version"/>
+<t>
+   The "Upgrade" header field is intended to provide a simple mechanism
+   for transitioning from HTTP/1.1 to some other protocol on the same
+   connection.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> send a list of protocol names in the Upgrade header field
+   of a request to invite the server to switch to one or more of the named
+   protocols, in order of descending preference, before sending
+   the final response. A server <bcp14>MAY</bcp14> ignore a received Upgrade header field
+   if it wishes to continue using the current protocol on that connection.
+   Upgrade cannot be used to insist on a protocol change.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Upgrade"/>
+  <x:ref>Upgrade</x:ref>          = #<x:ref>protocol</x:ref>
+
+  <x:ref>protocol</x:ref>         = <x:ref>protocol-name</x:ref> ["/" <x:ref>protocol-version</x:ref>]
+  <x:ref>protocol-name</x:ref>    = <x:ref>token</x:ref>
+  <x:ref>protocol-version</x:ref> = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   Although protocol names are registered with a preferred case,
+   recipients <bcp14>SHOULD</bcp14> use case-insensitive comparison when matching each
+   protocol-name to supported protocols.
+</t>
+<t>
+   A server that sends a <x:ref>101 (Switching Protocols)</x:ref> response
+   <bcp14>MUST</bcp14> send an Upgrade header field to indicate the new protocol(s) to
+   which the connection is being switched; if multiple protocol layers are
+   being switched, the sender <bcp14>MUST</bcp14> list the protocols in layer-ascending
+   order. A server <bcp14>MUST NOT</bcp14> switch to a protocol that was not indicated by
+   the client in the corresponding request's Upgrade header field.
+   A server <bcp14>MAY</bcp14> choose to ignore the order of preference indicated by the
+   client and select the new protocol(s) based on other factors, such as the
+   nature of the request or the current load on the server.
+</t>
+<t>
+   A server that sends a <x:ref>426 (Upgrade Required)</x:ref> response
+   <bcp14>MUST</bcp14> send an Upgrade header field to indicate the acceptable protocols,
+   in order of descending preference.
+</t>
+<t>
+   A server <bcp14>MAY</bcp14> send an Upgrade header field in any other response to
+   advertise that it implements support for upgrading to the listed protocols,
+   in order of descending preference, when appropriate for a future request.
+</t>
+<t>
+   The following is a hypothetical example sent by a client:
+</t>
+<sourcecode type="http-message">
+GET /hello HTTP/1.1
+Host: www.example.com
+Connection: upgrade
+Upgrade: websocket, IRC/6.9, RTA/x11
+
+</sourcecode>
+<t>
+   The capabilities and nature of the
+   application-level communication after the protocol change is entirely
+   dependent upon the new protocol(s) chosen. However, immediately after
+   sending the <x:ref>101 (Switching Protocols)</x:ref> response, the server is expected to continue responding to
+   the original request as if it had received its equivalent within the new
+   protocol (i.e., the server still has an outstanding request to satisfy
+   after the protocol has been changed, and is expected to do so without
+   requiring the request to be repeated).
+</t>
+<t>
+   For example, if the Upgrade header field is received in a GET request
+   and the server decides to switch protocols, it first responds
+   with a <x:ref>101 (Switching Protocols)</x:ref> message in HTTP/1.1 and
+   then immediately follows that with the new protocol's equivalent of a
+   response to a GET on the target resource.  This allows a connection to be
+   upgraded to protocols with the same semantics as HTTP without the
+   latency cost of an additional round trip.  A server <bcp14>MUST NOT</bcp14> switch
+   protocols unless the received message semantics can be honored by the new
+   protocol; an OPTIONS request can be honored by any protocol.
+</t>
+<t>
+   The following is an example response to the above hypothetical request:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 101 Switching Protocols
+Connection: upgrade
+Upgrade: websocket
+
+[... data stream switches to websocket with an appropriate response
+(as defined by new protocol) to the "GET /hello" request ...]
+</sourcecode>
+<t>
+   A sender of Upgrade <bcp14>MUST</bcp14> also send an "Upgrade" connection option in the
+   <x:ref>Connection</x:ref> header field (<xref target="field.connection"/>)
+   to inform intermediaries not to forward this field.
+   A server that receives an Upgrade header field in an HTTP/1.0 request
+   <bcp14>MUST</bcp14> ignore that Upgrade field.
+</t>
+<t>
+   A client cannot begin using an upgraded protocol on the connection until
+   it has completely sent the request message (i.e., the client can't change
+   the protocol it is sending in the middle of a message).
+   If a server receives both an Upgrade and an <x:ref>Expect</x:ref> header field
+   with the "100-continue" expectation (<xref target="field.expect"/>), the
+   server <bcp14>MUST</bcp14> send a <x:ref>100 (Continue)</x:ref> response before sending
+   a <x:ref>101 (Switching Protocols)</x:ref> response.
+</t>
+<t>
+   The Upgrade header field only applies to switching protocols on top of the
+   existing connection; it cannot be used to switch the underlying connection
+   (transport) protocol, nor to switch the existing communication to a
+   different connection. For those purposes, it is more appropriate to use a
+   <x:ref>3xx (Redirection)</x:ref> response (<xref target="status.3xx"/>).
+</t>
+<t>
+   This specification only defines the protocol name "HTTP" for use by
+   the family of Hypertext Transfer Protocols, as defined by the HTTP
+   version rules of <xref target="protocol.version"/> and future updates to this
+   specification. Additional protocol names ought to be registered using the
+   registration procedure defined in <xref target="upgrade.token.registry"/>.
+</t>
+</section>
+</section>
+
+<section title="Representation Data and Metadata" anchor="representation.data.and.metadata">
+<section title="Representation Data" anchor="representation.data">
+  <x:anchor-alias value="representation-data"/>
+<t>
+   The representation data associated with an HTTP message is
+   either provided as the content of the message or
+   referred to by the message semantics and the target
+   URI.  The representation data is in a format and encoding defined by
+   the representation metadata header fields.
+</t>
+<t>
+   The data type of the representation data is determined via the header fields
+   <x:ref>Content-Type</x:ref> and <x:ref>Content-Encoding</x:ref>.
+   These define a two-layer, ordered encoding model:
+</t>
+<artwork type="example">
+  representation-data := Content-Encoding( Content-Type( data ) )
+</artwork>
+</section>
+
+<section title="Representation Metadata" anchor="representation.metadata">
+  <x:anchor-alias value="representation-header"/>
+<t>
+   Representation header fields provide metadata about the representation.
+   When a message includes content, the representation header fields
+   describe how to interpret that data. In a response to a HEAD request, the
+   representation header fields describe the representation data that would
+   have been enclosed in the content if the same request had been a GET.
+</t>
+</section>
+
+<section title="Content-Type" anchor="field.content-type">
+  <x:anchor-alias value="header.content-type"/>
+  <iref primary="true" item="Fields" subitem="Content-Type" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-Type" x:for-anchor=""/><iref primary="true" item="Content-Type header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Type"/>
+<t>
+   The "Content-Type" header field indicates the media type of the
+   associated representation: either the representation enclosed in
+   the message content or the <x:ref>selected representation</x:ref>, as determined by the
+   message semantics.  The indicated media type defines both the data format
+   and how that data is intended to be processed by a recipient, within the
+   scope of the received message semantics, after any content codings
+   indicated by <x:ref>Content-Encoding</x:ref> are decoded.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Content-Type"/>
+  <x:ref>Content-Type</x:ref> = <x:ref>media-type</x:ref>
+</sourcecode>
+<t>
+   Media types are defined in <xref target="media.type"/>. An example of the
+   field is
+</t>
+<sourcecode type="http-message">
+Content-Type: text/html; charset=ISO-8859-4
+</sourcecode>
+<t>
+   A sender that generates a message containing content <bcp14>SHOULD</bcp14>
+   generate a Content-Type header field in that message unless the intended
+   media type of the enclosed representation is unknown to the sender.
+   If a Content-Type header field is not present, the recipient <bcp14>MAY</bcp14> either
+   assume a media type of
+   "application/octet-stream" (<xref target="RFC2046" x:fmt="," x:sec="4.5.1"/>)
+   or examine the data to determine its type.
+</t>
+<t>
+   In practice, resource owners do not always properly configure their origin
+   server to provide the correct Content-Type for a given representation.
+   Some user agents examine the content and, in certain cases,
+   override the received type (for example, see <xref target="Sniffing"/>).
+   This "MIME sniffing" risks drawing incorrect conclusions about the data,
+   which might expose the user to additional security risks
+   (e.g., "privilege escalation").
+   Furthermore, distinct media types often share a common data format,
+   differing only in how the data is intended to be processed, which is
+   impossible to distinguish by inspecting the data alone.
+   When sniffing is implemented, implementers are encouraged to provide a
+   means for the user to disable it.
+</t>
+<t>
+   Although Content-Type is defined as a singleton field, it is
+   sometimes incorrectly generated multiple times, resulting in a combined
+   field value that appears to be a list.
+   Recipients often attempt to handle this error by using the last
+   syntactically valid member of the list, leading to potential
+   interoperability and security issues if different implementations
+   have different error handling behaviors.
+</t>
+
+<section title="Media Type" anchor="media.type">
+  <x:anchor-alias value="media-type"/>
+  <x:anchor-alias value="type"/>
+  <x:anchor-alias value="subtype"/>
+<t>
+   HTTP uses media types <xref target="RFC2046"/> in the
+   <x:ref>Content-Type</x:ref> (<xref target="field.content-type"/>)
+   and <x:ref>Accept</x:ref> (<xref target="field.accept"/>) header fields in
+   order to provide open and extensible data typing and type negotiation.
+   Media types define both a data format and various processing models:
+   how to process that data in accordance with the message context.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="media-type"/><iref primary="true" item="Grammar" subitem="type"/><iref primary="true" item="Grammar" subitem="subtype"/>
+  <x:ref>media-type</x:ref> = <x:ref>type</x:ref> "/" <x:ref>subtype</x:ref> <x:ref>parameters</x:ref>
+  <x:ref>type</x:ref>       = <x:ref>token</x:ref>
+  <x:ref>subtype</x:ref>    = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   The type and subtype tokens are case-insensitive.
+</t>
+<t>
+   The type/subtype <bcp14>MAY</bcp14> be followed by semicolon-delimited parameters
+   (<xref target="parameter"/>) in the form of name/value pairs.
+   The presence or absence of a parameter might be significant to the
+   processing of a media type, depending on its definition within the media
+   type registry.
+   Parameter values might or might not be case-sensitive, depending on the
+   semantics of the parameter name.
+</t>
+<t>
+   For example, the following media types are equivalent in describing HTML
+   text data encoded in the UTF-8 character encoding scheme, but the first is
+   preferred for consistency (the "charset" parameter value is defined as
+   being case-insensitive in <xref target="RFC2046" x:fmt="," x:sec="4.1.2"/>):
+</t>
+<artwork type="example">
+  text/html;charset=utf-8
+  Text/HTML;Charset="utf-8"
+  text/html; charset="utf-8"
+  text/html;charset=UTF-8
+</artwork>
+<t>
+   Media types ought to be registered with IANA according to the
+   procedures defined in <xref target="BCP13"/>.
+</t>
+</section>
+
+<section title="Charset" anchor="charset">
+  <x:anchor-alias value="rule.charset"/>
+<t>
+   HTTP uses <x:dfn>charset</x:dfn> names to indicate or negotiate the
+   character encoding scheme (<xref target="RFC6365" sectionFormat="comma" section="2"/>)
+   of a textual representation. In the fields defined by this document,
+   charset names appear either in parameters (<x:ref>Content-Type</x:ref>),
+   or, for <x:ref>Accept-Encoding</x:ref>, in the form of a plain <x:ref>token</x:ref>.
+   In both cases, charset names are matched case-insensitively.
+</t>
+<t>
+   Charset names ought to be registered in the IANA "Character Sets" registry
+   (<eref target="https://www.iana.org/assignments/character-sets"/>)
+   according to the procedures defined in <xref target="RFC2978" x:fmt="of" x:sec="2"/>.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> In theory, charset names are defined by the "mime-charset" ABNF
+    rule defined in <xref target="RFC2978" x:fmt="of" x:sec="2.3"/> (as
+    corrected in <xref target="Err1912"/>). That rule allows two characters
+    that are not included in "token" ("{" and "}"), but no charset name
+    registered at the time of this writing includes braces
+    (see <xref target="Err5433"/>).
+  </t>
+</aside>
+</section>
+
+<section title="Multipart Types" anchor="multipart.types">
+<t>
+   MIME provides for a number of "multipart" types — encapsulations of
+   one or more representations within a single message body. All multipart
+   types share a common syntax, as defined in <xref target="RFC2046" x:sec="5.1.1" x:fmt="of"/>,
+   and include a boundary parameter as part of the media type
+   value. The message body is itself a protocol element; a sender <bcp14>MUST</bcp14>
+   generate only CRLF to represent line breaks between body parts.
+</t>
+<t>
+   HTTP message framing does not use the multipart boundary as an indicator
+   of message body length, though it might be used by implementations that
+   generate or process the content. For example, the "multipart/form-data"
+   type is often used for carrying form data in a request, as described in
+   <xref target="RFC7578"/>, and the "multipart/byteranges" type is defined
+   by this specification for use in some <x:ref>206 (Partial Content)</x:ref>
+   responses (see <xref target="status.206"/>).
+</t>
+</section>
+</section>
+
+<section title="Content-Encoding" anchor="field.content-encoding">
+  <x:anchor-alias value="header.content-encoding"/>
+  <iref primary="true" item="Fields" subitem="Content-Encoding" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-Encoding" x:for-anchor=""/><iref primary="true" item="Content-Encoding header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Encoding"/>
+<t>
+   The "Content-Encoding" header field indicates what content codings
+   have been applied to the representation, beyond those inherent in the media
+   type, and thus what decoding mechanisms have to be applied in order to
+   obtain data in the media type referenced by the <x:ref>Content-Type</x:ref>
+   header field.
+   Content-Encoding is primarily used to allow a representation's data to be
+   compressed without losing the identity of its underlying media type.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Content-Encoding"/>
+  <x:ref>Content-Encoding</x:ref> = #<x:ref>content-coding</x:ref>
+</sourcecode>
+<t>
+   An example of its use is
+</t>
+<sourcecode type="http-message">
+Content-Encoding: gzip
+</sourcecode>
+<t>
+   If one or more encodings have been applied to a representation, the sender
+   that applied the encodings <bcp14>MUST</bcp14> generate a Content-Encoding header field
+   that lists the content codings in the order in which they were applied.
+   Note that the coding named "identity" is reserved for its special role
+   in <x:ref>Accept-Encoding</x:ref> and thus <bcp14>SHOULD NOT</bcp14> be included.
+</t>
+<t>
+   Additional information about the encoding parameters can be provided
+   by other header fields not defined by this specification.
+</t>
+<t>
+   Unlike Transfer-Encoding (<xref target="HTTP11" x:rel="#field.transfer-encoding"/>), the codings listed
+   in Content-Encoding are a characteristic of the representation; the
+   representation is defined in terms of the coded form, and all other
+   metadata about the representation is about the coded form unless otherwise
+   noted in the metadata definition. Typically, the representation is only
+   decoded just prior to rendering or analogous usage.
+</t>
+<t>
+   If the media type includes an inherent encoding, such as a data format
+   that is always compressed, then that encoding would not be restated in
+   Content-Encoding even if it happens to be the same algorithm as one
+   of the content codings.  Such a content coding would only be listed if,
+   for some bizarre reason, it is applied a second time to form the
+   representation.  Likewise, an origin server might choose to publish the
+   same data as multiple representations that differ only in whether
+   the coding is defined as part of <x:ref>Content-Type</x:ref> or
+   Content-Encoding, since some user agents will behave differently in their
+   handling of each response (e.g., open a "Save as ..." dialog instead of
+   automatic decompression and rendering of content).
+</t>
+<t>
+   An origin server <bcp14>MAY</bcp14> respond with a status code of
+   <x:ref>415 (Unsupported Media Type)</x:ref> if a representation in the
+   request message has a content coding that is not acceptable.
+</t>
+
+<section title="Content Codings" anchor="content.codings">
+  <iref primary="true" item="content coding"/>
+  <iref primary="true" item="compress (content coding)"/>
+  <iref primary="true" item="x-compress (content coding)"/>
+  <iref primary="true" item="deflate (content coding)"/>
+  <iref primary="true" item="gzip (content coding)"/>
+  <iref primary="true" item="x-gzip (content coding)"/>
+  <x:anchor-alias value="content-coding"/>
+<t>
+   Content coding values indicate an encoding transformation that has
+   been or can be applied to a representation. Content codings are primarily
+   used to allow a representation to be compressed or otherwise usefully
+   transformed without losing the identity of its underlying media type
+   and without loss of information. Frequently, the representation is stored
+   in coded form, transmitted directly, and only decoded by the final recipient.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="content-coding"/>
+  <x:ref>content-coding</x:ref>   = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   All content codings are case-insensitive and ought to be registered
+   within the "HTTP Content Coding Registry", as described in
+   <xref target="content.coding.extensibility"/>
+</t>
+<t>
+   Content-coding values are used in the
+   <x:ref>Accept-Encoding</x:ref> (<xref target="field.accept-encoding"/>)
+   and <x:ref>Content-Encoding</x:ref> (<xref target="field.content-encoding"/>)
+   header fields.
+</t>
+
+<section title="Compress Coding" anchor="compress.coding">
+<iref item="compress (Coding Format)"/>
+<t>
+   The "compress" coding is an adaptive Lempel-Ziv-Welch (LZW) coding
+   <xref target="Welch"/> that is commonly produced by the UNIX file
+   compression program "compress".
+   A recipient <bcp14>SHOULD</bcp14> consider "x-compress" to be equivalent to "compress".
+</t>
+</section>
+
+<section title="Deflate Coding" anchor="deflate.coding">
+<iref item="deflate (Coding Format)"/>
+<t>
+   The "deflate" coding is a "zlib" data format <xref target="RFC1950"/>
+   containing a "deflate" compressed data stream <xref target="RFC1951"/>
+   that uses a combination of the Lempel-Ziv (LZ77) compression algorithm and
+   Huffman coding.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Some non-conformant implementations send the "deflate"
+    compressed data without the zlib wrapper.
+   </t>
+</aside>
+</section>
+
+<section title="Gzip Coding" anchor="gzip.coding">
+<iref item="gzip (Coding Format)"/>
+<t>
+   The "gzip" coding is an LZ77 coding with a 32-bit Cyclic Redundancy Check
+   (CRC) that is commonly
+   produced by the gzip file compression program <xref target="RFC1952"/>.
+   A recipient <bcp14>SHOULD</bcp14> consider "x-gzip" to be equivalent to "gzip".
+</t>
+</section>
+</section>
+</section>
+
+<section title="Content-Language" anchor="field.content-language">
+  <x:anchor-alias value="header.content-language"/>
+  <iref primary="true" item="Fields" subitem="Content-Language" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-Language" x:for-anchor=""/><iref primary="true" item="Content-Language header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Language"/>
+<t>
+   The "Content-Language" header field describes the natural
+   language(s) of the intended audience for the representation. Note that this might
+   not be equivalent to all the languages used within the representation.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Content-Language"/>
+  <x:ref>Content-Language</x:ref> = #<x:ref>language-tag</x:ref>
+</sourcecode>
+<t>
+   Language tags are defined in <xref target="language.tags"/>. The primary purpose of
+   Content-Language is to allow a user to identify and differentiate
+   representations according to the users' own preferred language. Thus, if the
+   content is intended only for a Danish-literate audience, the
+   appropriate field is
+</t>
+<sourcecode type="http-message">
+Content-Language: da
+</sourcecode>
+<t>
+   If no Content-Language is specified, the default is that the content
+   is intended for all language audiences. This might mean that the
+   sender does not consider it to be specific to any natural language,
+   or that the sender does not know for which language it is intended.
+</t>
+<t>
+   Multiple languages <bcp14>MAY</bcp14> be listed for content that is intended for
+   multiple audiences. For example, a rendition of the "Treaty of
+   Waitangi", presented simultaneously in the original Maori and English
+   versions, would call for
+</t>
+<sourcecode type="http-message">
+Content-Language: mi, en
+</sourcecode>
+<t>
+   However, just because multiple languages are present within a representation
+   does not mean that it is intended for multiple linguistic audiences.
+   An example would be a beginner's language primer, such as "A First
+   Lesson in Latin", which is clearly intended to be used by an
+   English-literate audience. In this case, the Content-Language would
+   properly only include "en".
+</t>
+<t>
+   Content-Language <bcp14>MAY</bcp14> be applied to any media type — it is not
+   limited to textual documents.
+</t>
+
+<section title="Language Tags" anchor="language.tags">
+  <x:anchor-alias value="language-tag"/>
+<t>
+   A language tag, as defined in <xref target="RFC5646"/>, identifies a
+   natural language spoken, written, or otherwise conveyed by human beings for
+   communication of information to other human beings. Computer languages are
+   explicitly excluded.
+</t>
+<t>
+   HTTP uses language tags within the <x:ref>Accept-Language</x:ref> and
+   <x:ref>Content-Language</x:ref> header fields.
+   <x:ref>Accept-Language</x:ref> uses the broader language-range production
+   defined in <xref target="field.accept-language"/>, whereas
+   <x:ref>Content-Language</x:ref> uses the language-tag production defined
+   below.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="language-tag"/>
+  <x:ref>language-tag</x:ref> = &lt;Language-Tag, see <xref target="RFC5646" x:sec="2.1"/>&gt;
+</sourcecode>
+<t>
+   A language tag is a sequence of one or more case-insensitive subtags, each
+   separated by a hyphen character ("-", %x2D).  In most cases, a language tag
+   consists of a primary language subtag that identifies a broad family of
+   related languages (e.g., "en" = English), which is optionally followed by a
+   series of subtags that refine or narrow that language's range (e.g.,
+   "en-CA" = the variety of English as communicated in Canada).
+   Whitespace is not allowed within a language tag.
+   Example tags include:
+</t>
+<artwork type="example">
+  fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN
+</artwork>
+<t>
+   See <xref target="RFC5646"/> for further information.
+</t>
+</section>
+</section>
+
+<section title="Content-Length" anchor="field.content-length">
+  <x:anchor-alias value="header.content-length"/>
+  <iref primary="true" item="Fields" subitem="Content-Length" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-Length" x:for-anchor=""/><iref primary="true" item="Content-Length header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Length"/>
+<t>
+   The "Content-Length" header field indicates the associated representation's
+   data length as a decimal non-negative integer number of octets.
+   When transferring a representation as content, Content-Length refers
+   specifically to the amount of data enclosed so that it can be used to
+   delimit framing (e.g., <xref target="HTTP11" x:rel="#body.content-length"/>).
+   In other cases, Content-Length indicates the selected representation's
+   current length, which can be used by recipients to estimate transfer time
+   or to compare with previously stored representations.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Content-Length"/>
+  <x:ref>Content-Length</x:ref> = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   An example is
+</t>
+<sourcecode type="http-message">
+Content-Length: 3495
+</sourcecode>
+<t>
+   A user agent <bcp14>SHOULD</bcp14> send Content-Length in a request when the method
+   defines a meaning for enclosed content and it is not sending
+   <x:ref>Transfer-Encoding</x:ref>.
+   For example, a user agent normally sends Content-Length in a POST request
+   even when the value is 0 (indicating empty content).
+   A user agent <bcp14>SHOULD NOT</bcp14> send a
+   Content-Length header field when the request message does not contain
+   content and the method semantics do not anticipate such data.
+</t>
+<t>
+   A server <bcp14>MAY</bcp14> send a Content-Length header field in a response to a HEAD
+   request (<xref target="HEAD"/>); a server <bcp14>MUST NOT</bcp14> send Content-Length in such a
+   response unless its field value equals the decimal number of octets that
+   would have been sent in the content of a response if the same
+   request had used the GET method.
+</t>
+<t>
+   A server <bcp14>MAY</bcp14> send a Content-Length header field in a
+   <x:ref>304 (Not Modified)</x:ref> response to a conditional GET request
+   (<xref target="status.304"/>); a server <bcp14>MUST NOT</bcp14> send Content-Length in such a
+   response unless its field value equals the decimal number of octets that
+   would have been sent in the content of a <x:ref>200 (OK)</x:ref>
+   response to the same request.
+</t>
+<t>
+   A server <bcp14>MUST NOT</bcp14> send a Content-Length header field in any response
+   with a status code of
+   <x:ref>1xx (Informational)</x:ref> or <x:ref>204 (No Content)</x:ref>.
+   A server <bcp14>MUST NOT</bcp14> send a Content-Length header field in any
+   <x:ref>2xx (Successful)</x:ref> response to a CONNECT request (<xref target="CONNECT"/>).
+</t>
+<t>
+   Aside from the cases defined above, in the absence of Transfer-Encoding,
+   an origin server <bcp14>SHOULD</bcp14> send a Content-Length header field when the
+   content size is known prior to sending the complete header section.
+   This will allow downstream recipients to measure transfer progress,
+   know when a received message is complete, and potentially reuse the
+   connection for additional requests.
+</t>
+<t>
+   Any Content-Length field value greater than or equal to zero is valid.
+   Since there is no predefined limit to the length of content, a
+   recipient <bcp14>MUST</bcp14> anticipate potentially large decimal numerals and
+   prevent parsing errors due to integer conversion overflows
+   or precision loss due to integer conversion
+   (<xref target="attack.protocol.element.length"/>).
+</t>
+<t>
+   Because Content-Length is used for message delimitation in HTTP/1.1,
+   its field value can impact how the message is parsed by downstream
+   recipients even when the immediate connection is not using HTTP/1.1.
+   If the message is forwarded by a downstream intermediary, a Content-Length
+   field value that is inconsistent with the received message framing might
+   cause a security failure due to request smuggling or response splitting.
+</t>
+<t>
+   As a result, a sender <bcp14>MUST NOT</bcp14> forward a message with a
+   Content-Length header field value that is known to be incorrect.
+</t>
+<t>
+   Likewise, a sender <bcp14>MUST NOT</bcp14> forward a message with a Content-Length
+   header field value that does not match the ABNF above, with one exception:
+   a recipient of a Content-Length header field value consisting of the same
+   decimal value repeated as a comma-separated list (e.g,
+   "Content-Length: 42, 42") <bcp14>MAY</bcp14> either reject the message as invalid or
+   replace that invalid field value with a single instance of the decimal
+   value, since this likely indicates that a duplicate was generated or
+   combined by an upstream message processor.
+</t>
+</section>
+
+<section title="Content-Location" anchor="field.content-location">
+  <x:anchor-alias value="header.content-location"/>
+  <iref primary="true" item="Fields" subitem="Content-Location" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-Location" x:for-anchor=""/><iref primary="true" item="Content-Location header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Location"/>
+<t>
+   The "Content-Location" header field references a URI that can be used
+   as an identifier for a specific resource corresponding to the
+   representation in this message's content.
+   In other words, if one were to perform a GET request on this URI at the time
+   of this message's generation, then a <x:ref>200 (OK)</x:ref> response would
+   contain the same representation that is enclosed as content in this message.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Content-Location"/>
+  <x:ref>Content-Location</x:ref> = <x:ref>absolute-URI</x:ref> / <x:ref>partial-URI</x:ref>
+</sourcecode>
+<t>
+   The field value is either an <x:ref>absolute-URI</x:ref> or a
+   <x:ref>partial-URI</x:ref>. In the latter case (<xref target="uri"/>),
+   the referenced URI is relative to the target URI
+   (<xref target="URI" x:fmt="," x:sec="5"/>).
+</t>
+<t>
+   The Content-Location value is not a replacement for the target URI
+   (<xref target="target.resource"/>).  It is representation metadata.
+   It has the same syntax and semantics as the header field of the same name
+   defined for MIME body parts in <xref target="RFC2557" x:fmt="of" x:sec="4"/>.
+   However, its appearance in an HTTP message has some special implications
+   for HTTP recipients.
+</t>
+<t>
+   If Content-Location is included in a <x:ref>2xx (Successful)</x:ref>
+   response message and its value refers (after conversion to absolute form)
+   to a URI that is the same as the target URI, then
+   the recipient <bcp14>MAY</bcp14> consider the content to be a current representation of
+   that resource at the time indicated by the message origination date.
+   For a GET (<xref target="GET"/>) or HEAD (<xref target="HEAD"/>) request,
+   this is the same as the default semantics when no Content-Location is
+   provided by the server.
+   For a state-changing request like PUT (<xref target="PUT"/>) or
+   POST (<xref target="POST"/>), it implies that the server's response
+   contains the new representation of that resource, thereby distinguishing it
+   from representations that might only report about the action
+   (e.g., "It worked!").
+   This allows authoring applications to update their local copies without
+   the need for a subsequent GET request.
+</t>
+<t>
+   If Content-Location is included in a <x:ref>2xx (Successful)</x:ref>
+   response message and its field value refers to a URI that differs from the
+   target URI, then the origin server claims that the URI
+   is an identifier for a different resource corresponding to the enclosed
+   representation. Such a claim can only be trusted if both identifiers share
+   the same resource owner, which cannot be programmatically determined via
+   HTTP.
+</t>
+<ul>
+  <li>For a response to a GET or HEAD request, this is an indication that the
+     target URI refers to a resource that is subject to content
+     negotiation and the Content-Location field value is a more specific
+     identifier for the <x:ref>selected representation</x:ref>.</li>
+  <li>For a <x:ref>201 (Created)</x:ref> response to a state-changing method,
+     a Content-Location field value that is identical to the
+     <x:ref>Location</x:ref> field value indicates that this content is a
+     current representation of the newly created resource.</li>
+  <li>Otherwise, such a Content-Location indicates that this content is a
+     representation reporting on the requested action's status and that the
+     same report is available (for future access with GET) at the given URI.
+     For example, a purchase transaction made via a POST request might
+     include a receipt document as the content of the <x:ref>200 (OK)</x:ref>
+     response; the Content-Location field value provides an identifier for
+     retrieving a copy of that same receipt in the future.</li>
+</ul>
+<t>
+   A user agent that sends Content-Location in a request message is stating
+   that its value refers to where the user agent originally obtained the
+   content of the enclosed representation (prior to any modifications made by
+   that user agent).  In other words, the user agent is providing a back link
+   to the source of the original representation.
+</t>
+<t>
+   An origin server that receives a Content-Location field in a request
+   message <bcp14>MUST</bcp14> treat the information as transitory request context rather
+   than as metadata to be saved verbatim as part of the representation.
+   An origin server <bcp14>MAY</bcp14> use that context to guide in processing the
+   request or to save it for other uses, such as within source links or
+   versioning metadata. However, an origin server <bcp14>MUST NOT</bcp14> use such context
+   information to alter the request semantics.
+</t>
+<t>
+   For example, if a client makes a PUT request on a negotiated resource and
+   the origin server accepts that PUT (without redirection), then the new
+   state of that resource is expected to be consistent with the one
+   representation supplied in that PUT; the Content-Location cannot be used as
+   a form of reverse content selection identifier to update only one of the
+   negotiated representations. If the user agent had wanted the latter
+   semantics, it would have applied the PUT directly to the Content-Location
+   URI.
+</t>
+</section>
+
+<section title="Validator Fields" anchor="response.validator">
+   <iref primary="true" item="metadata"/>
+   <iref primary="true" item="validator"/>
+   <iref item="selected representation"/>
+<t>
+   Resource metadata is referred to as a <x:dfn>validator</x:dfn> if it
+   can be used within a precondition (<xref target="preconditions"/>) to
+   make a conditional request (<xref target="conditional.requests"/>).
+   Validator fields convey a current validator for the
+   <x:ref>selected representation</x:ref>
+   (<xref target="representations"/>).
+</t>
+<t>
+   In responses to safe requests, validator fields describe the selected
+   representation chosen by the origin server while handling the response.
+   Note that, depending on the method and status code semantics, the
+   selected representation for a given response is not
+   necessarily the same as the representation enclosed as response content.
+</t>
+<t>
+   In a successful response to a state-changing request, validator fields
+   describe the new representation that has replaced the prior
+   <x:ref>selected representation</x:ref> as a result of processing the
+   request.
+</t>
+<t>
+   For example, an ETag field in a <x:ref>201 (Created)</x:ref> response
+   communicates the entity tag of the newly created resource's
+   representation, so that the entity tag can be used as a validator in
+   later conditional requests to prevent the "lost update" problem.
+</t>
+<t>
+   This specification defines two forms of metadata that are commonly used
+   to observe resource state and test for preconditions: modification dates
+   (<xref target="field.last-modified"/>) and opaque entity tags
+   (<xref target="field.etag"/>).
+   Additional metadata that reflects resource state
+   has been defined by various extensions of HTTP, such as Web Distributed
+   Authoring and Versioning <xref target="WEBDAV"/>, that are beyond the
+   scope of this specification.
+</t>
+
+<section title="Weak versus Strong" anchor="weak.and.strong.validators">
+   <iref primary="true" item="validator" subitem="weak"/>
+   <iref primary="true" item="validator" subitem="strong"/>
+<t>
+   Validators come in two flavors: strong or weak.  Weak validators are easy
+   to generate but are far less useful for comparisons.  Strong validators
+   are ideal for comparisons but can be very difficult (and occasionally
+   impossible) to generate efficiently.  Rather than impose that all forms
+   of resource adhere to the same strength of validator, HTTP exposes the
+   type of validator in use and imposes restrictions on when weak validators
+   can be used as preconditions.
+</t>
+<t>
+   A <x:dfn>strong validator</x:dfn> is representation metadata that changes value whenever
+   a change occurs to the representation data that would be observable in the
+   content of a <x:ref>200 (OK)</x:ref> response to GET.
+</t>
+<t>
+   A strong validator might change for reasons other than a change to the
+   representation data, such as when a
+   semantically significant part of the representation metadata is changed
+   (e.g., <x:ref>Content-Type</x:ref>), but it is in the best interests of the
+   origin server to only change the value when it is necessary to invalidate
+   the stored responses held by remote caches and authoring tools.
+</t>
+<t>
+   Cache entries might persist for arbitrarily long periods, regardless
+   of expiration times.  Thus, a cache might attempt to validate an
+   entry using a validator that it obtained in the distant past.
+   A strong validator is unique across all versions of all
+   representations associated with a particular resource over time.
+   However, there is no implication of uniqueness across representations
+   of different resources (i.e., the same strong validator might be
+   in use for representations of multiple resources at the same time
+   and does not imply that those representations are equivalent).
+</t>
+<t>
+   There are a variety of strong validators used in practice.  The best are
+   based on strict revision control, wherein each change to a representation
+   always results in a unique node name and revision identifier being assigned
+   before the representation is made accessible to GET.
+   A collision-resistant hash
+   function applied to the representation data is also sufficient if the data
+   is available prior to the response header fields being sent and the digest
+   does not need to be recalculated every time a validation request is
+   received.  However, if a resource has distinct representations that differ
+   only in their metadata, such as might occur with content negotiation over
+   media types that happen to share the same data format, then the origin
+   server needs to incorporate additional information in the validator to
+   distinguish those representations.
+</t>
+<t>
+   In contrast, a <x:dfn>weak validator</x:dfn> is representation metadata
+   that might not change for every change to the representation data. This
+   weakness might be due to limitations in how the value is calculated
+   (e.g., clock resolution), an inability to ensure uniqueness for all
+   possible representations of the resource, or a desire of the resource
+   owner to group representations by some self-determined set of
+   equivalency rather than unique sequences of data.
+</t>
+<t>
+   An origin server <bcp14>SHOULD</bcp14> change a weak entity tag whenever it
+   considers prior representations to be unacceptable as a substitute for
+   the current representation. In other words, a weak entity tag ought to
+   change whenever the origin server wants caches to invalidate old
+   responses.
+</t>
+<t>
+   For example, the representation of a weather report that changes in
+   content every second, based on dynamic measurements, might be grouped
+   into sets of equivalent representations (from the origin server's
+   perspective) with the same weak validator in order to allow cached
+   representations to be valid for a reasonable period of time (perhaps
+   adjusted dynamically based on server load or weather quality).
+   Likewise, a representation's modification time, if defined with only
+   one-second resolution, might be a weak validator if it is possible
+   for the representation to be modified twice during a single second and
+   retrieved between those modifications.
+</t>
+<t>
+   Likewise, a validator is weak if it is shared by two or more
+   representations of a given resource at the same time, unless those
+   representations have identical representation data. For example, if the
+   origin server sends the same validator for a representation with a gzip
+   content coding applied as it does for a representation with no content
+   coding, then that validator is weak. However, two simultaneous
+   representations might share the same strong validator if they differ only
+   in the representation metadata, such as when two different media types are
+   available for the same representation data.
+</t>
+<t>
+   Strong validators are usable for all conditional requests, including cache
+   validation, partial content ranges, and "lost update" avoidance.
+   Weak validators are only usable when the client does not require exact
+   equality with previously obtained representation data, such as when
+   validating a cache entry or limiting a web traversal to recent changes.
+</t>
+</section>
+
+<section title="Last-Modified" anchor="field.last-modified">
+  <x:anchor-alias value="header.last-modified"/>
+  <iref primary="true" item="Fields" subitem="Last-Modified" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Last-Modified" x:for-anchor=""/><iref primary="true" item="Last-Modified header field" x:for-anchor=""/>
+  <x:anchor-alias value="Last-Modified"/>
+<t>
+   The "Last-Modified" header field in a response provides a timestamp
+   indicating the date and time at which the origin server believes the
+   <x:ref>selected representation</x:ref> was last modified, as determined at the conclusion
+   of handling the request.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Last-Modified"/>
+  <x:ref>Last-Modified</x:ref> = <x:ref>HTTP-date</x:ref>
+</sourcecode>
+<t>
+   An example of its use is
+</t>
+<sourcecode type="http-message">
+Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
+</sourcecode>
+
+<section title="Generation" anchor="lastmod.generation">
+<t>
+   An origin server <bcp14>SHOULD</bcp14> send Last-Modified for any selected
+   representation for which a last modification date can be reasonably
+   and consistently determined, since its use in conditional requests
+   and evaluating cache freshness (<xref target="CACHING"/>) can
+   substantially reduce unnecessary transfers and significantly
+   improve service availability and scalability.
+</t>
+<t>
+   A representation is typically the sum of many parts behind the
+   resource interface.  The last-modified time would usually be
+   the most recent time that any of those parts were changed.
+   How that value is determined for any given resource is an
+   implementation detail beyond the scope of this specification.
+</t>
+<t>
+   An origin server <bcp14>SHOULD</bcp14> obtain the Last-Modified value of the
+   representation as close as possible to the time that it generates the
+   <x:ref>Date</x:ref> field value for its response. This allows a recipient to
+   make an accurate assessment of the representation's modification time,
+   especially if the representation changes near the time that the
+   response is generated.
+</t>
+<t>
+   An origin server with a clock (as defined in <xref target="http.date"/>)
+   <bcp14>MUST NOT</bcp14> generate a Last-Modified date that is later than the
+   server's time of message origination
+   (<x:ref>Date</x:ref>, <xref target="field.date"/>).
+   If the last modification time is derived from implementation-specific
+   metadata that evaluates to some time in the future, according to the
+   origin server's clock, then the origin server <bcp14>MUST</bcp14> replace that
+   value with the message origination date. This prevents a future
+   modification date from having an adverse impact on cache validation.
+</t>
+<t>
+   An origin server without a clock <bcp14>MUST NOT</bcp14> generate a Last-Modified
+   date for a response unless that date value was assigned to the resource
+   by some other system (presumably one with a clock).
+</t>
+</section>
+
+<section title="Comparison" anchor="lastmod.comparison">
+<t>
+   A Last-Modified time, when used as a validator in a request, is
+   implicitly weak unless it is possible to deduce that it is strong,
+   using the following rules:
+</t>
+<ul>
+   <li>The validator is being compared by an origin server to the
+       actual current validator for the representation and,</li>
+   <li>That origin server reliably knows that the associated representation did
+       not change twice during the second covered by the presented
+       validator;</li>
+</ul>
+<t>
+   or
+</t>
+<ul>
+   <li>The validator is about to be used by a client in an
+       <x:ref>If-Modified-Since</x:ref>,
+       <x:ref>If-Unmodified-Since</x:ref>, or <x:ref>If-Range</x:ref> header
+       field, because the client has a cache entry for the associated
+       representation, and</li>
+   <li>That cache entry includes a <x:ref>Date</x:ref> value which is
+       at least one second after the Last-Modified value and
+       the client has reason to believe that they were generated by the
+       same clock or that there is enough difference between the Last-Modified
+       and Date values to make clock synchronization issues unlikely;</li>
+</ul>
+<t>
+   or
+</t>
+<ul>
+   <li>The validator is being compared by an intermediate cache to the
+       validator stored in its cache entry for the representation, and</li>
+   <li>That cache entry includes a <x:ref>Date</x:ref> value which is
+       at least one second after the Last-Modified value and
+       the cache has reason to believe that they were generated by the
+       same clock or that there is enough difference between the Last-Modified
+       and Date values to make clock synchronization issues unlikely.</li>
+</ul>
+<t>
+   This method relies on the fact that if two different responses were
+   sent by the origin server during the same second, but both had the
+   same Last-Modified time, then at least one of those responses would
+   have a <x:ref>Date</x:ref> value equal to its Last-Modified time.
+</t>
+</section>
+</section>
+
+<section title="ETag" anchor="field.etag">
+  <x:anchor-alias value="header.etag"/>
+  <iref primary="true" item="Fields" subitem="ETag" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="ETag" x:for-anchor=""/><iref primary="true" item="Trailer Fields" subitem="ETag" x:for-anchor=""/><iref primary="true" item="ETag field" x:for-anchor=""/>
+  <x:anchor-alias value="ETag"/>
+  <x:anchor-alias value="entity-tag"/>
+  <x:anchor-alias value="opaque-tag"/>
+  <x:anchor-alias value="weak"/>
+  <x:anchor-alias value="etagc"/>
+<t>
+   The "ETag" field in a response provides the current entity tag for
+   the <x:ref>selected representation</x:ref>, as determined at the conclusion of handling
+   the request.
+   An entity tag is an opaque validator for differentiating between
+   multiple representations of the same resource, regardless of whether
+   those multiple representations are due to resource state changes over
+   time, content negotiation resulting in multiple representations being
+   valid at the same time, or both. An entity tag consists of an opaque
+   quoted string, possibly prefixed by a weakness indicator.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="ETag"/><iref primary="true" item="Grammar" subitem="entity-tag"/><iref primary="true" item="Grammar" subitem="weak"/><iref primary="true" item="Grammar" subitem="opaque-tag"/><iref primary="true" item="Grammar" subitem="etagc"/>
+  <x:ref>ETag</x:ref>       = <x:ref>entity-tag</x:ref>
+
+  <x:ref>entity-tag</x:ref> = [ <x:ref>weak</x:ref> ] <x:ref>opaque-tag</x:ref>
+  <x:ref>weak</x:ref>       = %s"W/"
+  <x:ref>opaque-tag</x:ref> = <x:ref>DQUOTE</x:ref> *<x:ref>etagc</x:ref> <x:ref>DQUOTE</x:ref>
+  <x:ref>etagc</x:ref>      = %x21 / %x23-7E / <x:ref>obs-text</x:ref>
+             ; <x:ref>VCHAR</x:ref> except double quotes, plus obs-text
+</sourcecode>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Previously, opaque-tag was defined to be a quoted-string
+    (<xref target="RFC2616" x:fmt="," x:sec="3.11"/>); thus, some recipients
+    might perform backslash unescaping. Servers therefore ought to avoid
+    backslash characters in entity tags.
+  </t>
+</aside>
+<t>
+   An entity tag can be more reliable for validation than a modification
+   date in situations where it is inconvenient to store modification
+   dates, where the one-second resolution of HTTP-date values is not
+   sufficient, or where modification dates are not consistently maintained.
+</t>
+<t>
+  Examples:
+</t>
+<sourcecode type="http-message">
+ETag: "xyzzy"
+ETag: W/"xyzzy"
+ETag: ""
+</sourcecode>
+<t>
+   An entity tag can be either a weak or strong validator, with
+   strong being the default.  If an origin server provides an entity tag
+   for a representation and the generation of that entity tag does not satisfy
+   all of the characteristics of a strong validator
+   (<xref target="weak.and.strong.validators"/>), then the origin server
+   <bcp14>MUST</bcp14> mark the entity tag as weak by prefixing its opaque value
+   with "W/" (case-sensitive).
+</t>
+<t>
+   A sender <bcp14>MAY</bcp14> send the ETag field in a trailer section (see
+   <xref target="trailer.fields"/>). However, since trailers are often
+   ignored, it is preferable to send ETag as a header field unless the
+   entity tag is generated while sending the content.
+</t>
+
+<section title="Generation" anchor="entity.tag.generation">
+<t>
+   The principle behind entity tags is that only the service author
+   knows the implementation of a resource well enough to select the
+   most accurate and efficient validation mechanism for that resource,
+   and that any such mechanism can be mapped to a simple sequence of
+   octets for easy comparison.  Since the value is opaque, there is no
+   need for the client to be aware of how each entity tag is constructed.
+</t>
+<t>
+   For example, a resource that has implementation-specific versioning
+   applied to all changes might use an internal revision number, perhaps
+   combined with a variance identifier for content negotiation, to
+   accurately differentiate between representations.
+   Other implementations might use a collision-resistant hash of
+   representation content, a combination of various file attributes, or
+   a modification timestamp that has sub-second resolution.
+</t>
+<t>
+   An origin server <bcp14>SHOULD</bcp14> send an ETag for any selected representation
+   for which detection of changes can be reasonably and consistently
+   determined, since the entity tag's use in conditional requests and
+   evaluating cache freshness (<xref target="CACHING"/>) can
+   substantially reduce unnecessary transfers and significantly
+   improve service availability, scalability, and reliability.
+</t>
+</section>
+
+<section title="Comparison" anchor="entity.tag.comparison">
+  <x:anchor-alias value="validator.comparison"/>
+  <x:anchor-alias value="strong comparison"/>
+  <x:anchor-alias value="weak comparison"/>
+<t>
+   There are two entity tag comparison functions, depending on whether or not
+   the comparison context allows the use of weak validators:
+</t>
+<dl>
+  <dt>
+    <x:dfn>Strong comparison</x:dfn>:
+  </dt>
+  <dd>
+    two entity tags are equivalent if both are not weak and their opaque-tags
+    match character-by-character.
+  </dd>
+  <dt>
+    <x:dfn>Weak comparison</x:dfn>:
+  </dt>
+  <dd>
+    two entity tags are equivalent if their opaque-tags match
+    character-by-character, regardless of either or both being tagged as "weak".
+  </dd>
+</dl>
+<t>
+   The example below shows the results for a set of entity tag pairs and both
+   the weak and strong comparison function results:
+</t>
+<table align="left">
+  <thead>
+    <tr>
+      <th>ETag 1</th>
+      <th>ETag 2</th>
+      <th>Strong Comparison</th>
+      <th>Weak Comparison</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>W/"1"</td>
+      <td>W/"1"</td>
+      <td>no match</td>
+      <td>match</td>
+    </tr>
+    <tr>
+      <td>W/"1"</td>
+      <td>W/"2"</td>
+      <td>no match</td>
+      <td>no match</td>
+    </tr>
+    <tr>
+      <td>W/"1"</td>
+      <td>"1"</td>
+      <td>no match</td>
+      <td>match</td>
+    </tr>
+    <tr>
+      <td>"1"</td>
+      <td>"1"</td>
+      <td>match</td>
+      <td>match</td>
+    </tr>
+  </tbody>
+</table>
+</section>
+
+<section title="Example: Entity Tags Varying on Content-Negotiated Resources" anchor="example.entity.tag.vs.conneg">
+<t>
+   Consider a resource that is subject to content negotiation
+   (<xref target="content.negotiation"/>), and where the representations sent in response to
+   a GET request vary based on the <x:ref>Accept-Encoding</x:ref> request
+   header field (<xref target="field.accept-encoding"/>):
+</t>
+<t>
+   &gt;&gt; Request:
+</t>
+<sourcecode type="http-message">
+GET /index HTTP/1.1
+Host: www.example.com
+Accept-Encoding: gzip
+
+</sourcecode>
+<t>
+   In this case, the response might or might not use the gzip content coding.
+   If it does not, the response might look like:
+</t>
+<t>
+   &gt;&gt; Response:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 200 OK
+Date: Fri, 26 Mar 2010 00:05:00 GMT
+ETag: "123-a"
+Content-Length: <x:length-of target="exbody2"/>
+Vary: Accept-Encoding
+Content-Type: text/plain
+
+<x:span anchor="exbody2">Hello World!
+Hello World!
+Hello World!
+Hello World!
+Hello World!
+</x:span></sourcecode>
+<t>
+   An alternative representation that does use gzip content coding would be:
+</t>
+<t>
+   &gt;&gt; Response:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 200 OK
+Date: Fri, 26 Mar 2010 00:05:00 GMT
+ETag: "123-b"
+Content-Length: 43
+Vary: Accept-Encoding
+Content-Type: text/plain
+Content-Encoding: gzip
+
+<spanx>...binary data...</spanx></sourcecode>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Content codings are a property of the representation data,
+    so a strong entity tag for a content-encoded representation has to be
+    distinct from the entity tag of an unencoded representation to prevent
+    potential conflicts during cache updates and range requests. In contrast,
+    transfer codings (<xref target="HTTP11" x:rel="#transfer.codings"/>) apply only during message transfer
+    and do not result in distinct entity tags.
+  </t>
+</aside>
+</section>
+</section>
+</section>
+</section>
+
+<section title="Methods" anchor="methods">
+
+<section title="Overview" anchor="method.overview">
+  <x:anchor-alias value="method"/>
+<t>
+   The request method token is the primary source of request semantics;
+   it indicates the purpose for which the client has made this request
+   and what is expected by the client as a successful result.
+</t>
+<t>
+   The request method's semantics might be further specialized by the
+   semantics of some header fields when present in a request
+   if those additional semantics do not conflict with the method.
+   For example, a client can send conditional request header fields
+   (<xref target="preconditions"/>) to make the requested
+   action conditional on the current state of the target resource.
+</t>
+<t>
+   HTTP is designed to be usable as an interface to distributed
+   object systems. The request method invokes an action to be applied to
+   a <x:ref>target resource</x:ref> in much the same way that a remote
+   method invocation can be sent to an identified object.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="method"/>
+  <x:ref>method</x:ref> = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   The method token is case-sensitive because it might be used as a gateway
+   to object-based systems with case-sensitive method names. By convention,
+   standardized methods are defined in all-uppercase US-ASCII letters.
+</t>
+<t>
+   Unlike distributed objects, the standardized request methods in HTTP are
+   not resource-specific, since uniform interfaces provide for better
+   visibility and reuse in network-based systems <xref target="REST"/>.
+   Once defined, a standardized method ought to have the same semantics when
+   applied to any resource, though each resource determines for itself
+   whether those semantics are implemented or allowed.
+</t>
+<t>
+   This specification defines a number of standardized methods that are
+   commonly used in HTTP, as outlined by the following table.
+</t>
+<table align="left" anchor="table.of.methods">
+  <thead>
+    <tr>
+      <th>Method Name</th>
+      <th>Description</th>
+      <th>Section</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>GET</td>
+      <td>Transfer a current representation of the target resource.</td>
+      <td><xref target="GET" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>HEAD</td>
+      <td>Same as GET, but do not transfer the response content.</td>
+      <td><xref target="HEAD" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>POST</td>
+      <td>Perform resource-specific processing on the request content.</td>
+      <td><xref target="POST" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>PUT</td>
+      <td>Replace all current representations of the target resource with
+        the request content.</td>
+      <td><xref target="PUT" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>DELETE</td>
+      <td>Remove all current representations of the target resource.</td>
+      <td><xref target="DELETE" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>CONNECT</td>
+      <td>Establish a tunnel to the server identified by the target resource.</td>
+      <td><xref target="CONNECT" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>OPTIONS</td>
+      <td>Describe the communication options for the target resource.</td>
+      <td><xref target="OPTIONS" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>TRACE</td>
+      <td>Perform a message loop-back test along the path to the target resource.</td>
+      <td><xref target="TRACE" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+<t>
+   All general-purpose servers <bcp14>MUST</bcp14> support the methods GET and HEAD.
+   All other methods are <bcp14>OPTIONAL</bcp14>.
+</t>
+<t>
+   The set of methods allowed by a target resource can be listed in an
+   <x:ref>Allow</x:ref> header field (<xref target="field.allow"/>).
+   However, the set of allowed methods can change dynamically.
+   An origin server that receives a request method that is unrecognized or
+   not implemented <bcp14>SHOULD</bcp14> respond with the
+   <x:ref>501 (Not Implemented)</x:ref> status code.
+   An origin server that receives a request method that is recognized and
+   implemented, but not allowed for the target resource, <bcp14>SHOULD</bcp14> respond
+   with the <x:ref>405 (Method Not Allowed)</x:ref> status code.
+</t>
+<t>
+   Additional methods, outside the scope of this specification, have been
+   specified for use in HTTP. All such methods ought to be registered
+   within the "Hypertext Transfer Protocol (HTTP) Method Registry",
+   as described in <xref target="method.extensibility"/>.
+</t>
+</section>
+
+<section title="Common Method Properties" anchor="method.properties">
+
+<section title="Safe Methods" anchor="safe.methods">
+  <iref item="safe" primary="true"/>
+  <x:anchor-alias value="safe"/>
+<t>
+   Request methods are considered <x:dfn>safe</x:dfn> if
+   their defined semantics are essentially read-only; i.e., the client does
+   not request, and does not expect, any state change on the origin server
+   as a result of applying a safe method to a target resource.  Likewise,
+   reasonable use of a safe method is not expected to cause any harm,
+   loss of property, or unusual burden on the origin server.
+</t>
+<t>
+   This definition of safe methods does not prevent an implementation from
+   including behavior that is potentially harmful, that is not entirely read-only,
+   or that causes side effects while invoking a safe method.  What is
+   important, however, is that the client did not request that additional
+   behavior and cannot be held accountable for it.  For example,
+   most servers append request information to access log files at the
+   completion of every response, regardless of the method, and that is
+   considered safe even though the log storage might become full and cause
+   the server to fail.  Likewise, a safe request initiated by selecting an
+   advertisement on the Web will often have the side effect of charging an
+   advertising account.
+</t>
+<t>
+   Of the request methods defined by this specification, the
+   <x:ref>GET</x:ref>, <x:ref>HEAD</x:ref>, <x:ref>OPTIONS</x:ref>, and
+   <x:ref>TRACE</x:ref> methods are defined to be safe.
+</t>
+<t>
+   The purpose of distinguishing between safe and unsafe methods is to
+   allow automated retrieval processes (spiders) and cache performance
+   optimization (pre-fetching) to work without fear of causing harm.
+   In addition, it allows a user agent to apply appropriate constraints
+   on the automated use of unsafe methods when processing potentially
+   untrusted content.
+</t>
+<t>
+   A user agent <bcp14>SHOULD</bcp14> distinguish between safe and unsafe methods when
+   presenting potential actions to a user, such that the user can be made
+   aware of an unsafe action before it is requested.
+</t>
+<t>
+   When a resource is constructed such that parameters within the target URI
+   have the effect of selecting an action, it is the resource
+   owner's responsibility to ensure that the action is consistent with the
+   request method semantics.
+   For example, it is common for Web-based content editing software
+   to use actions within query parameters, such as "page?do=delete".
+   If the purpose of such a resource is to perform an unsafe action, then
+   the resource owner <bcp14>MUST</bcp14> disable or disallow that action when it is
+   accessed using a safe request method. Failure to do so will result in
+   unfortunate side effects when automated processes perform a GET on
+   every URI reference for the sake of link maintenance, pre-fetching,
+   building a search index, etc.
+</t>
+</section>
+
+<section title="Idempotent Methods" anchor="idempotent.methods">
+<iref item="idempotent" primary="true"/>
+<t>
+   A request method is considered
+   <x:dfn anchor="idempotent">idempotent</x:dfn>
+   if the intended effect on the server of multiple identical requests with
+   that method is the same as the effect for a single such request.
+   Of the request methods defined by this
+   specification, <x:ref>PUT</x:ref>, <x:ref>DELETE</x:ref>, and safe request
+   methods are idempotent.
+</t>
+<t>
+   Like the definition of safe, the idempotent property only applies to
+   what has been requested by the user; a server is free to log each request
+   separately, retain a revision control history, or implement other
+   non-idempotent side effects for each idempotent request.
+</t>
+<t>
+   Idempotent methods are distinguished because the request can be repeated
+   automatically if a communication failure occurs before the client is
+   able to read the server's response.  For example, if a client sends a PUT
+   request and the underlying connection is closed before any response is
+   received, then the client can establish a new connection and retry the
+   idempotent request. It knows that repeating the request will have
+   the same intended effect, even if the original request succeeded, though
+   the response might differ.
+</t>
+<t>
+   A client <bcp14>SHOULD NOT</bcp14> automatically retry a request with a non-idempotent
+   method unless it has some means to know that the request semantics are
+   actually idempotent, regardless of the method, or some means to detect that
+   the original request was never applied.
+</t>
+<t>
+   For example, a user agent can repeat a POST request automatically if it
+   knows (through design or configuration) that the request is safe for that
+   resource. Likewise, a user agent designed specifically to operate on
+   a version control repository might be able to recover from partial failure
+   conditions by checking the target resource revision(s) after a failed
+   connection, reverting or fixing any changes that were partially applied,
+   and then automatically retrying the requests that failed.
+</t>
+<t>
+   Some clients take a riskier approach and attempt to guess when an
+   automatic retry is possible. For example, a client might automatically
+   retry a POST request if the underlying transport connection closed before
+   any part of a response is received, particularly if an idle persistent
+   connection was used.
+</t>
+<t>
+   A proxy <bcp14>MUST NOT</bcp14> automatically retry non-idempotent requests.
+   A client <bcp14>SHOULD NOT</bcp14> automatically retry a failed automatic retry.
+</t>
+</section>
+
+<section title="Methods and Caching" anchor="cacheable.methods">
+<t>
+   For a cache to store and use a response, the associated method needs to
+   explicitly allow caching and to detail under what conditions a response can
+   be used to satisfy subsequent requests; a method definition that does not
+   do so cannot be cached. For additional requirements see <xref target="CACHING"/>.
+</t>
+<t>
+   This specification defines caching semantics for GET, HEAD, and POST,
+   although the overwhelming majority of cache implementations only support
+   GET and HEAD.
+</t>
+</section>
+</section>
+
+<section title="Method Definitions" anchor="method.definitions">
+
+<section title="GET" anchor="GET">
+  <rdf:Description>
+    <safe xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</safe>
+    <idempotent xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</idempotent>
+  </rdf:Description>
+  <iref primary="true" item="GET method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="GET" x:for-anchor=""/>
+<t>
+   The GET method requests transfer of a current
+   <x:ref>selected representation</x:ref> for the
+   <x:ref>target resource</x:ref>.
+   A successful response reflects the quality of "sameness" identified by
+   the target URI (<xref target="URI" section="1.2.2"/>). Hence,
+   retrieving identifiable information via HTTP is usually performed by
+   making a GET request on an identifier associated with the potential for
+   providing that information in a <x:ref>200 (OK)</x:ref> response.
+</t>
+<t>
+   GET is the primary mechanism of information retrieval and the focus of
+   almost all performance optimizations. Applications that produce a URI for
+   each important resource can benefit from those optimizations while enabling
+   their reuse by other applications, creating a network effect that promotes
+   further expansion of the Web.
+</t>
+<t>
+   It is tempting to think of resource identifiers as remote file system
+   pathnames and of representations as being a copy of the contents of such
+   files. In fact, that is how many resources are implemented (see
+   <xref target="attack.pathname"/> for related security considerations).
+   However, there are no such limitations in practice.
+</t>
+<t>
+   The HTTP interface for
+   a resource is just as likely to be implemented as a tree of content
+   objects, a programmatic view on various database records, or a gateway to
+   other information systems. Even when the URI mapping mechanism is tied to a
+   file system, an origin server might be configured to execute the files with
+   the request as input and send the output as the representation rather than
+   transfer the files directly. Regardless, only the origin server needs to
+   know how each resource identifier corresponds to an implementation
+   and how that implementation manages to select and send a current
+   representation of the target resource.
+</t>
+<t>
+   A client can alter the semantics of GET to be a "range request", requesting
+   transfer of only some part(s) of the selected representation, by sending a
+   <x:ref>Range</x:ref> header field in the request (<xref target="field.range"/>).
+</t>
+<t>
+   Although request message framing is independent of the method used,
+   content received in a GET request has no generally defined semantics,
+   cannot alter the meaning or target of the request, and might lead some
+   implementations to reject the request and close the connection because of
+   its potential as a request smuggling attack
+   (<xref target="HTTP11" x:rel="#request.smuggling"/>).
+   A client <bcp14>SHOULD NOT</bcp14> generate content in a GET request unless it is
+   made directly to an origin server that has previously indicated,
+   in or out of band, that such a request has a purpose and will be adequately
+   supported. An origin server <bcp14>SHOULD NOT</bcp14> rely on private agreements to
+   receive content, since participants in HTTP communication are often
+   unaware of intermediaries along the request chain.
+</t>
+<t>
+   The response to a GET request is cacheable; a cache <bcp14>MAY</bcp14> use it to satisfy
+   subsequent GET and HEAD requests unless otherwise indicated by the
+   Cache-Control header field (<xref target="CACHING" x:rel="#field.cache-control"/>).
+</t>
+<t>
+   When information retrieval is performed with a mechanism that constructs a
+   target URI from user-provided information, such as the query fields of a
+   form using GET, potentially sensitive data might be provided that would not
+   be appropriate for disclosure within a URI
+   (see <xref target="sensitive.information.in.uris"/>). In some cases, the
+   data can be filtered or transformed such that it would not reveal such
+   information. In others, particularly when there is no benefit from caching
+   a response, using the POST method (<xref target="POST"/>) instead of GET
+   can transmit such information in the request content rather than within
+   the target URI.
+</t>
+</section>
+
+<section title="HEAD" anchor="HEAD">
+  <rdf:Description>
+    <safe xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</safe>
+    <idempotent xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</idempotent>
+  </rdf:Description>
+  <iref primary="true" item="HEAD method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="HEAD" x:for-anchor=""/>
+<t>
+   The HEAD method is identical to GET except that the server <bcp14>MUST NOT</bcp14>
+   send content in the response. HEAD is used to obtain metadata about the
+   <x:ref>selected representation</x:ref> without transferring its
+   representation data, often for the sake of testing hypertext links or
+   finding recent modifications.
+</t>
+<t>
+   The server <bcp14>SHOULD</bcp14> send the same header fields in response to a HEAD
+   request as it would have sent if the request method had been GET.
+   However, a server <bcp14>MAY</bcp14> omit header fields for which a value is determined
+   only while generating the content. For example, some servers buffer a
+   dynamic response to GET until a minimum amount of data is generated so
+   that they can more efficiently delimit small responses or make late
+   decisions with regard to content selection. Such a response to GET might
+   contain <x:ref>Content-Length</x:ref> and <x:ref>Vary</x:ref> fields, for
+   example, that are not generated within a HEAD response. These minor
+   inconsistencies are considered preferable to generating and discarding the
+   content for a HEAD request, since HEAD is usually requested for the
+   sake of efficiency.
+</t>
+<t>
+   Although request message framing is independent of the method used,
+   content received in a HEAD request has no generally defined semantics,
+   cannot alter the meaning or target of the request, and might lead some
+   implementations to reject the request and close the connection because of
+   its potential as a request smuggling attack
+   (<xref target="HTTP11" x:rel="#request.smuggling"/>).
+   A client <bcp14>SHOULD NOT</bcp14> generate content in a HEAD request unless it is
+   made directly to an origin server that has previously indicated,
+   in or out of band, that such a request has a purpose and will be adequately
+   supported. An origin server <bcp14>SHOULD NOT</bcp14> rely on private agreements to
+   receive content, since participants in HTTP communication are often
+   unaware of intermediaries along the request chain.
+</t>
+<t>
+   The response to a HEAD request is cacheable; a cache <bcp14>MAY</bcp14> use it to
+   satisfy subsequent HEAD requests unless otherwise indicated by the
+   Cache-Control header field (<xref target="CACHING" x:rel="#field.cache-control"/>).
+   A HEAD response might also affect previously cached responses to GET;
+   see <xref target="CACHING" x:rel="#head.effects"/>.
+</t>
+</section>
+
+<section title="POST" anchor="POST">
+  <iref primary="true" item="POST method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="POST" x:for-anchor=""/>
+<t>
+   The POST method requests that the <x:ref>target resource</x:ref> process
+   the representation enclosed in the request according to the resource's own
+   specific semantics. For example, POST is used for the following functions
+   (among others):
+</t>
+<ul>
+   <li>Providing a block of data, such as the fields entered into an HTML
+      form, to a data-handling process;</li>
+   <li>Posting a message to a bulletin board, newsgroup, mailing list, blog,
+      or similar group of articles;</li>
+   <li>Creating a new resource that has yet to be identified by the origin
+      server; and</li>
+   <li>Appending data to a resource's existing representation(s).</li>
+</ul>
+<t>
+   An origin server indicates response semantics by choosing an appropriate
+   status code depending on the result of processing the POST request;
+   almost all of the status codes defined by this specification could be
+   received in a response to POST (the exceptions being <x:ref>206 (Partial Content)</x:ref>,
+   <x:ref>304 (Not Modified)</x:ref>, and <x:ref>416 (Range Not Satisfiable)</x:ref>).
+</t>
+<t>
+   If one or more resources has been created on the origin server as a result
+   of successfully processing a POST request, the origin server <bcp14>SHOULD</bcp14> send
+   a <x:ref>201 (Created)</x:ref> response containing a <x:ref>Location</x:ref>
+   header field that provides an identifier for the primary resource created
+   (<xref target="field.location"/>) and a representation that describes the
+   status of the request while referring to the new resource(s).
+</t>
+<t>
+   Responses to POST requests are only cacheable when they include explicit
+   freshness information (see <xref target="CACHING" x:rel="#calculating.freshness.lifetime"/>) and a
+   <x:ref>Content-Location</x:ref> header field that has the same value as
+   the POST's target URI (<xref target="field.content-location"/>).  A cached POST response can be reused
+   to satisfy a later GET or HEAD request. In contrast, a POST request cannot
+   be satisfied by a cached POST response because POST is potentially unsafe;
+   see <xref target="CACHING" x:rel="#constructing.responses.from.caches"/>.
+</t>
+<t>
+   If the result of processing a POST would be equivalent to a representation
+   of an existing resource, an origin server <bcp14>MAY</bcp14> redirect the user agent to
+   that resource by sending a <x:ref>303 (See Other)</x:ref> response with the
+   existing resource's identifier in the <x:ref>Location</x:ref> field.
+   This has the benefits of providing the user agent a resource identifier
+   and transferring the representation via a method more amenable to shared
+   caching, though at the cost of an extra request if the user agent does not
+   already have the representation cached.
+</t>
+</section>
+
+<section title="PUT" anchor="PUT">
+  <rdf:Description>
+    <idempotent xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</idempotent>
+  </rdf:Description>
+  <iref primary="true" item="PUT method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="PUT" x:for-anchor=""/>
+<t>
+   The PUT method requests that the state of the <x:ref>target resource</x:ref>
+   be created or replaced with the state defined by the representation
+   enclosed in the request message content.  A successful PUT of a given
+   representation would suggest that a subsequent GET on that same target
+   resource will result in an equivalent representation being sent in
+   a <x:ref>200 (OK)</x:ref> response.  However, there is no guarantee that
+   such a state change will be observable, since the target resource might be
+   acted upon by other user agents in parallel, or might be subject to dynamic
+   processing by the origin server, before any subsequent GET is received.
+   A successful response only implies that the user agent's intent was
+   achieved at the time of its processing by the origin server.
+</t>
+<t>
+   If the target resource does not have a current representation and
+   the PUT successfully creates one, then the origin server <bcp14>MUST</bcp14> inform
+   the user agent by sending a <x:ref>201 (Created)</x:ref> response.  If the
+   target resource does have a current representation and that representation is
+   successfully modified in accordance with the state of the enclosed
+   representation, then the origin server <bcp14>MUST</bcp14> send either a
+   <x:ref>200 (OK)</x:ref> or a <x:ref>204 (No Content)</x:ref> response to
+   indicate successful completion of the request.
+</t>
+<t>
+   An origin server <bcp14>SHOULD</bcp14> verify that the PUT representation is consistent
+   with its configured constraints for the target resource. For example, if
+   an origin server determines a resource's representation metadata based on
+   the URI, then the origin server needs to ensure that the content received
+   in a successful PUT request is consistent with that metadata. When a PUT
+   representation is inconsistent with the target resource, the origin
+   server <bcp14>SHOULD</bcp14> either make them consistent, by transforming the
+   representation or changing the resource configuration, or respond
+   with an appropriate error message containing sufficient information
+   to explain why the representation is unsuitable.  The
+   <x:ref>409 (Conflict)</x:ref> or <x:ref>415 (Unsupported Media Type)</x:ref>
+   status codes are suggested, with the latter being specific to constraints on
+   <x:ref>Content-Type</x:ref> values.
+</t>
+<t>
+   For example, if the target resource is configured to always have a
+   <x:ref>Content-Type</x:ref> of "text/html" and the representation being PUT
+   has a Content-Type of "image/jpeg", the origin server ought to do one of:
+</t>
+<ol type="a">
+   <li>reconfigure the target resource to reflect the new media type;</li>
+   <li>transform the PUT representation to a format consistent with that
+       of the resource before saving it as the new resource state; or,</li>
+   <li>reject the request with a <x:ref>415 (Unsupported Media Type)</x:ref>
+       response indicating that the target resource is limited to "text/html",
+       perhaps including a link to a different resource that would be a
+       suitable target for the new representation.</li>
+</ol>
+<t>
+   HTTP does not define exactly how a PUT method affects the state
+   of an origin server beyond what can be expressed by the intent of
+   the user agent request and the semantics of the origin server response.
+   It does not define what a resource might be, in any sense of that
+   word, beyond the interface provided via HTTP.  It does not define
+   how resource state is "stored", nor how such storage might change
+   as a result of a change in resource state, nor how the origin server
+   translates resource state into representations.  Generally speaking,
+   all implementation details behind the resource interface are
+   intentionally hidden by the server.
+</t>
+<t>
+   This extends to how header and trailer fields are stored; while common
+   header fields like <x:ref>Content-Type</x:ref> will typically be stored
+   and returned upon subsequent GET requests, header and trailer field
+   handling is specific to the resource that received the request. As a result,
+   an origin server <bcp14>SHOULD</bcp14> ignore unrecognized header and trailer fields
+   received in a PUT request (i.e., not save them as part of the resource
+   state).
+</t>
+<t>
+   An origin server <bcp14>MUST NOT</bcp14> send a validator field
+   (<xref target="response.validator"/>), such as an <x:ref>ETag</x:ref> or
+   <x:ref>Last-Modified</x:ref> field, in a successful response to PUT unless
+   the request's representation data was saved without any transformation
+   applied to the content (i.e., the resource's new representation data is
+   identical to the content received in the PUT request) and the
+   validator field value reflects the new representation.
+   This requirement allows a user agent to know when the representation it
+   sent (and retains in memory) is the result of the PUT, and thus it doesn't
+   need to be retrieved again from the origin server. The new validator(s)
+   received in the response can be used for future conditional requests in
+   order to prevent accidental overwrites (<xref target="preconditions"/>).
+</t>
+<t>
+   The fundamental difference between the POST and PUT methods is
+   highlighted by the different intent for the enclosed representation.
+   The target resource in a POST request is intended to handle the
+   enclosed representation according to the resource's own semantics,
+   whereas the enclosed representation in a PUT request is defined as
+   replacing the state of the target resource. Hence, the intent of PUT is
+   idempotent and visible to intermediaries, even though the exact effect is
+   only known by the origin server.
+</t>
+<t>
+   Proper interpretation of a PUT request presumes that the user agent knows
+   which target resource is desired. A service that selects a proper URI on
+   behalf of the client, after receiving a state-changing request, <bcp14>SHOULD</bcp14> be
+   implemented using the POST method rather than PUT. If the origin server
+   will not make the requested PUT state change to the target resource and
+   instead wishes to have it applied to a different resource, such as when the
+   resource has been moved to a different URI, then the origin server <bcp14>MUST</bcp14>
+   send an appropriate <x:ref>3xx (Redirection)</x:ref> response; the
+   user agent <bcp14>MAY</bcp14> then make its own decision regarding whether or not to
+   redirect the request.
+</t>
+<t>
+   A PUT request applied to the target resource can have side effects
+   on other resources.  For example, an article might have a URI for
+   identifying "the current version" (a resource) that is separate
+   from the URIs identifying each particular version (different
+   resources that at one point shared the same state as the current version
+   resource).  A successful PUT request on "the current version" URI might
+   therefore create a new version resource in addition to changing the
+   state of the target resource, and might also cause links to be added
+   between the related resources.
+</t>
+<t>
+   Some origin servers support use of the <x:ref>Content-Range</x:ref> header
+   field (<xref target="field.content-range"/>) as a request modifier to
+   perform a partial PUT, as described in <xref target="partial.PUT"/>.
+</t>
+<t>
+   Responses to the PUT method are not cacheable. If a successful PUT request
+   passes through a cache that has one or more stored responses for the
+   target URI, those stored responses will be invalidated
+   (see <xref target="CACHING" x:rel="#invalidation"/>).
+</t>
+</section>
+
+<section title="DELETE" anchor="DELETE">
+  <rdf:Description>
+    <idempotent xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</idempotent>
+  </rdf:Description>
+  <iref primary="true" item="DELETE method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="DELETE" x:for-anchor=""/>
+<t>
+   The DELETE method requests that the origin server remove the association
+   between the <x:ref>target resource</x:ref> and its current functionality.
+   In effect, this method is similar to the "rm" command in UNIX: it expresses a
+   deletion operation on the URI mapping of the origin server rather than an
+   expectation that the previously associated information be deleted.
+</t>
+<t>
+   If the target resource has one or more current representations, they might
+   or might not be destroyed by the origin server, and the associated storage
+   might or might not be reclaimed, depending entirely on the nature of the
+   resource and its implementation by the origin server (which are beyond the
+   scope of this specification). Likewise, other implementation aspects of a
+   resource might need to be deactivated or archived as a result of a DELETE,
+   such as database or gateway connections. In general, it is assumed that the
+   origin server will only allow DELETE on resources for which it has a
+   prescribed mechanism for accomplishing the deletion.
+</t>
+<t>
+   Relatively few resources allow the DELETE method — its primary use
+   is for remote authoring environments, where the user has some direction
+   regarding its effect. For example, a resource that was previously created
+   using a PUT request, or identified via the Location header field after a
+   <x:ref>201 (Created)</x:ref> response to a POST request, might allow a
+   corresponding DELETE request to undo those actions.  Similarly, custom
+   user agent implementations that implement an authoring function, such as
+   revision control clients using HTTP for remote operations, might use
+   DELETE based on an assumption that the server's URI space has been crafted
+   to correspond to a version repository.
+</t>
+<t>
+   If a DELETE method is successfully applied, the origin server <bcp14>SHOULD</bcp14> send
+</t>
+<ul>
+  <li>a <x:ref>202 (Accepted)</x:ref> status code if the action will likely succeed but
+   has not yet been enacted,</li>
+  <li>a <x:ref>204 (No Content)</x:ref> status code if the action has been
+   enacted and no further information is to be supplied, or</li>
+  <li>a <x:ref>200 (OK)</x:ref> status code if the action has been enacted and
+   the response message includes a representation describing the status.</li>
+</ul>
+<t>
+   Although request message framing is independent of the method used,
+   content received in a DELETE request has no generally defined semantics,
+   cannot alter the meaning or target of the request, and might lead some
+   implementations to reject the request and close the connection because of
+   its potential as a request smuggling attack
+   (<xref target="HTTP11" x:rel="#request.smuggling"/>).
+   A client <bcp14>SHOULD NOT</bcp14> generate content in a DELETE request unless it is
+   made directly to an origin server that has previously indicated,
+   in or out of band, that such a request has a purpose and will be adequately
+   supported. An origin server <bcp14>SHOULD NOT</bcp14> rely on private agreements to
+   receive content, since participants in HTTP communication are often
+   unaware of intermediaries along the request chain.
+</t>
+<t>
+   Responses to the DELETE method are not cacheable. If a successful DELETE
+   request passes through a cache that has one or more stored responses for
+   the target URI, those stored responses will be invalidated (see
+   <xref target="CACHING" x:rel="#invalidation"/>).
+</t>
+</section>
+
+<section title="CONNECT" anchor="CONNECT">
+  <iref primary="true" item="CONNECT method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="CONNECT" x:for-anchor=""/>
+<t>
+   The CONNECT method requests that the recipient establish a tunnel to the
+   destination origin server identified by the request target and, if
+   successful, thereafter restrict its behavior to blind forwarding of
+   data, in both directions, until the tunnel is closed.
+   Tunnels are commonly used to create an end-to-end virtual connection,
+   through one or more proxies, which can then be secured using TLS
+   (Transport Layer Security, <xref target="TLS13"/>).
+</t>
+<t>
+   CONNECT uses a special form of request target, unique to this method,
+   consisting of only the host and port number of the tunnel destination,
+   separated by a colon. There is no default port; a client <bcp14>MUST</bcp14> send the
+   port number even if the CONNECT request is based on a URI reference that
+   contains an authority component with an elided port
+   (<xref target="uri.references"/>). For example,
+</t>
+<sourcecode type="http-message">
+CONNECT server.example.com:80 HTTP/1.1
+Host: server.example.com
+
+</sourcecode>
+<t>
+   A server <bcp14>MUST</bcp14> reject a CONNECT request that targets an empty or invalid
+   port number, typically by responding with a 400 (Bad Request) status code.
+</t>
+<t>
+   Because CONNECT changes the request/response nature of an HTTP connection,
+   specific HTTP versions might have different ways of mapping its semantics
+   into the protocol's wire format.
+</t>
+<t>
+   CONNECT is intended for use in requests to a proxy.
+   The recipient can establish a tunnel either by directly connecting to
+   the server identified by the request target or, if configured to use
+   another proxy, by forwarding the CONNECT request to the next inbound proxy.
+   An origin server <bcp14>MAY</bcp14> accept a CONNECT request, but most origin servers
+   do not implement CONNECT.
+</t>
+<t>
+   Any <x:ref>2xx (Successful)</x:ref> response indicates
+   that the sender (and all inbound proxies) will switch to tunnel mode
+   immediately after the response header section; data received after that
+   header section is from the server identified by the request target.
+   Any response other than a successful response indicates that the tunnel
+   has not yet been formed.
+</t>
+<t>
+   A tunnel is closed when a tunnel intermediary detects that either side
+   has closed its connection: the intermediary <bcp14>MUST</bcp14> attempt to send any
+   outstanding data that came from the closed side to the other side, close
+   both connections, and then discard any remaining data left undelivered.
+</t>
+<t>
+   Proxy authentication might be used to establish the
+   authority to create a tunnel.  For example,
+</t>
+<sourcecode type="http-message">
+CONNECT server.example.com:443 HTTP/1.1
+Host: server.example.com:443
+Proxy-Authorization: basic aGVsbG86d29ybGQ=
+
+</sourcecode>
+<t>
+   There are significant risks in establishing a tunnel to arbitrary servers,
+   particularly when the destination is a well-known or reserved TCP port that
+   is not intended for Web traffic. For example, a CONNECT to
+   "example.com:25" would suggest that the proxy connect to the reserved
+   port for SMTP traffic; if allowed, that could trick the proxy into
+   relaying spam email. Proxies that support CONNECT <bcp14>SHOULD</bcp14> restrict its
+   use to a limited set of known ports or a configurable list of safe
+   request targets.
+</t>
+<t>
+   A server <bcp14>MUST NOT</bcp14> send any <x:ref>Transfer-Encoding</x:ref> or
+   <x:ref>Content-Length</x:ref> header fields in a
+   <x:ref>2xx (Successful)</x:ref> response to CONNECT.
+   A client <bcp14>MUST</bcp14> ignore any Content-Length or Transfer-Encoding header
+   fields received in a successful response to CONNECT.
+</t>
+<t>
+   A CONNECT request message does not have content. The interpretation of
+   data sent after the header section of the CONNECT request message is
+   specific to the version of HTTP in use.
+</t>
+<t>
+   Responses to the CONNECT method are not cacheable.
+</t>
+</section>
+
+<section title="OPTIONS" anchor="OPTIONS">
+  <rdf:Description>
+    <safe xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</safe>
+    <idempotent xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</idempotent>
+  </rdf:Description>
+  <iref primary="true" item="OPTIONS method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="OPTIONS" x:for-anchor=""/>
+<t>
+   The OPTIONS method requests information about the communication options
+   available for the target resource, at either the origin server or an
+   intervening intermediary. This method allows a client to determine the
+   options and/or requirements associated with a resource, or the capabilities
+   of a server, without implying a resource action.
+</t>
+<t>
+   An OPTIONS request with an asterisk ("*") as the request target
+   (<xref target="target.resource"/>) applies to the server in general rather than to a
+   specific resource. Since a server's communication options typically depend
+   on the resource, the "*" request is only useful as a "ping" or "no-op"
+   type of method; it does nothing beyond allowing the client to test
+   the capabilities of the server. For example, this can be used to test
+   a proxy for HTTP/1.1 conformance (or lack thereof).
+</t>
+<t>
+   If the request target is not an asterisk, the OPTIONS request applies
+   to the options that are available when communicating with the target
+   resource.
+</t>
+<t>
+   A server generating a successful response to OPTIONS <bcp14>SHOULD</bcp14> send any
+   header that might indicate optional features implemented by the
+   server and applicable to the target resource (e.g., <x:ref>Allow</x:ref>),
+   including potential extensions not defined by this specification.
+   The response content, if any, might also describe the communication options
+   in a machine or human-readable representation. A standard format for such a
+   representation is not defined by this specification, but might be defined by
+   future extensions to HTTP.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> send a <x:ref>Max-Forwards</x:ref> header field in an
+   OPTIONS request to target a specific recipient in the request chain (see
+   <xref target="field.max-forwards"/>). A proxy <bcp14>MUST NOT</bcp14> generate a
+   Max-Forwards header field while forwarding a request unless that request
+   was received with a Max-Forwards field.
+</t>
+<t>
+   A client that generates an OPTIONS request containing content
+   <bcp14>MUST</bcp14> send a valid <x:ref>Content-Type</x:ref> header field describing
+   the representation media type. Note that this specification does not define
+   any use for such content.
+</t>
+<t>
+   Responses to the OPTIONS method are not cacheable.
+</t>
+</section>
+
+<section title="TRACE" anchor="TRACE">
+  <rdf:Description>
+    <safe xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</safe>
+    <idempotent xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</idempotent>
+  </rdf:Description>
+  <iref primary="true" item="TRACE method" x:for-anchor=""/>
+  <iref primary="true" item="Method" subitem="TRACE" x:for-anchor=""/>
+<t>
+   The TRACE method requests a remote, application-level loop-back of the
+   request message. The final recipient of the request <bcp14>SHOULD</bcp14> reflect the
+   message received, excluding some fields described below, back to the client
+   as the content of a <x:ref>200 (OK)</x:ref> response. The "message/http"
+   format (<xref target="HTTP11" x:rel="#media.type.message.http"/>) is one way to do so.
+   The final recipient is either the origin server or the first server to
+   receive a <x:ref>Max-Forwards</x:ref> value of zero (0) in the request
+   (<xref target="field.max-forwards"/>).
+</t>
+<t>
+   A client <bcp14>MUST NOT</bcp14> generate fields in a TRACE request containing
+   sensitive data that might be disclosed by the response. For example, it
+   would be foolish for a user agent to send stored user credentials
+   (<xref target="authentication"/>) or cookies <xref target="COOKIE"/> in a TRACE
+   request. The final recipient of the request <bcp14>SHOULD</bcp14> exclude any request
+   fields that are likely to contain sensitive data when that recipient
+   generates the response content.
+</t>
+<t>
+   TRACE allows the client to see what is being received at the other
+   end of the request chain and use that data for testing or diagnostic
+   information. The value of the <x:ref>Via</x:ref> header field (<xref target="field.via"/>)
+   is of particular interest, since it acts as a trace of the request chain.
+   Use of the <x:ref>Max-Forwards</x:ref> header field allows the client to
+   limit the length of the request chain, which is useful for testing a chain
+   of proxies forwarding messages in an infinite loop.
+</t>
+<t>
+   A client <bcp14>MUST NOT</bcp14> send content in a TRACE request.
+</t>
+<t>
+   Responses to the TRACE method are not cacheable.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Message Context" anchor="context">
+
+<section title="Request Context Fields" anchor="request.context">
+  <x:anchor-alias value="request-header"/>
+<t>
+   The request header fields below provide additional information about the
+   request context, including information about the user, user agent, and
+   resource behind the request.
+</t>
+
+<section title="Expect" anchor="field.expect">
+  <x:anchor-alias value="header.expect"/>
+  <iref primary="true" item="Fields" subitem="Expect" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Expect" x:for-anchor=""/><iref primary="true" item="Expect header field" x:for-anchor=""/>
+  <iref primary="true" item="100-continue (expect value)"/>
+  <x:anchor-alias value="Expect"/>
+  <x:anchor-alias value="expectation"/>
+  <x:anchor-alias value="100-continue"/>
+<t>
+   The "Expect" header field in a request indicates a certain set of
+   behaviors (expectations) that need to be supported by the server in
+   order to properly handle this request.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Expect"/>
+  <x:ref>Expect</x:ref> =      #<x:ref>expectation</x:ref>
+  <x:ref>expectation</x:ref> = <x:ref>token</x:ref> [ "=" ( <x:ref>token</x:ref> / <x:ref>quoted-string</x:ref> ) <x:ref>parameters</x:ref> ]
+</sourcecode>
+<t>
+   The Expect field value is case-insensitive.
+</t>
+<t>
+   The only expectation defined by this specification is "100-continue"
+   (with no defined parameters).
+</t>
+<t>
+   A server that receives an Expect field value containing a member other than
+   <x:ref>100-continue</x:ref> <bcp14>MAY</bcp14> respond with a
+   <x:ref>417 (Expectation Failed)</x:ref> status code to indicate that the
+   unexpected expectation cannot be met.
+</t>
+<t>
+   A <x:dfn>100-continue</x:dfn> expectation informs recipients that the
+   client is about to send (presumably large) content in this request
+   and wishes to receive a <x:ref>100 (Continue)</x:ref> interim response if
+   the method, target URI, and header fields are not sufficient to cause an immediate
+   success, redirect, or error response. This allows the client to wait for an
+   indication that it is worthwhile to send the content before actually
+   doing so, which can improve efficiency when the data is huge or
+   when the client anticipates that an error is likely (e.g., when sending a
+   state-changing method, for the first time, without previously verified
+   authentication credentials).
+</t>
+<t>
+   For example, a request that begins with
+</t>
+<sourcecode type="http-message">
+PUT /somewhere/fun HTTP/1.1
+Host: origin.example.com
+Content-Type: video/h264
+Content-Length: 1234567890987
+Expect: 100-continue
+
+</sourcecode>
+<t>
+   allows the origin server to immediately respond with an error message, such
+   as <x:ref>401 (Unauthorized)</x:ref> or <x:ref>405 (Method Not Allowed)</x:ref>,
+   before the client starts filling the pipes with an unnecessary data
+   transfer.
+</t>
+<t>
+   Requirements for clients:
+</t>
+<ul>
+   <li>
+    A client <bcp14>MUST NOT</bcp14> generate a 100-continue expectation in a request that
+    does not include content.
+   </li>
+   <li>
+    A client that will wait for a <x:ref>100 (Continue)</x:ref> response
+    before sending the request content <bcp14>MUST</bcp14> send an
+    <x:ref>Expect</x:ref> header field containing a 100-continue expectation.
+   </li>
+   <li>
+    A client that sends a 100-continue expectation is not required to wait
+    for any specific length of time; such a client <bcp14>MAY</bcp14> proceed to send the
+    content even if it has not yet received a response. Furthermore,
+    since <x:ref>100 (Continue)</x:ref> responses cannot be sent through an
+    HTTP/1.0 intermediary, such a client <bcp14>SHOULD NOT</bcp14> wait for an indefinite
+    period before sending the content.
+   </li>
+   <li>
+    A client that receives a <x:ref>417 (Expectation Failed)</x:ref> status
+    code in response to a request containing a 100-continue expectation
+    <bcp14>SHOULD</bcp14> repeat that request without a 100-continue expectation, since
+    the 417 response merely indicates that the response chain does not
+    support expectations (e.g., it passes through an HTTP/1.0 server).
+   </li>
+</ul>
+<t>
+   Requirements for servers:
+</t>
+<ul>
+   <li>
+    A server that receives a 100-continue expectation in an HTTP/1.0 request
+    <bcp14>MUST</bcp14> ignore that expectation.
+   </li>
+   <li>
+    A server <bcp14>MAY</bcp14> omit sending a <x:ref>100 (Continue)</x:ref> response if
+    it has already received some or all of the content for the
+    corresponding request, or if the framing indicates that there is no
+    content.
+   </li>
+   <li>
+    A server that sends a <x:ref>100 (Continue)</x:ref> response <bcp14>MUST</bcp14>
+    ultimately send a final status code, once it receives and processes the
+    request content, unless the connection is closed prematurely.
+   </li>
+   <li>
+    A server that responds with a final status code before reading the
+    entire request content <bcp14>SHOULD</bcp14> indicate whether it intends to
+    close the connection (e.g., see <xref target="HTTP11" x:rel="#persistent.tear-down"/>) or
+    continue reading the request content.
+   </li>
+</ul>
+<t>
+   Upon receiving an HTTP/1.1 (or later) request that has a method, target URI,
+   and complete header section that contains a 100-continue expectation and
+   an indication that request content will follow, an origin server <bcp14>MUST</bcp14>
+   send either:
+</t>
+<ul>
+  <li>an immediate response with a final status code, if that status can be
+      determined by examining just the method, target URI, and header fields, or</li>
+  <li>an immediate <x:ref>100 (Continue)</x:ref> response to encourage the client
+      to send the request content.</li>
+</ul>
+<t>
+   The origin server <bcp14>MUST NOT</bcp14> wait for the content
+   before sending the <x:ref>100 (Continue)</x:ref> response.
+</t>
+<t>
+   Upon receiving an HTTP/1.1 (or later) request that has a method, target URI,
+   and complete header section that contains a 100-continue expectation and
+   indicates a request content will follow, a proxy <bcp14>MUST</bcp14> either:
+</t>
+<ul>
+  <li>send an immediate
+   response with a final status code, if that status can be determined by
+   examining just the method, target URI, and header fields, or</li>
+  <li>forward the request toward the origin server by sending a corresponding
+   request-line and header section to the next inbound server.</li>
+</ul>
+<t>
+   If the proxy believes (from configuration or past interaction) that the
+   next inbound server only supports HTTP/1.0, the proxy <bcp14>MAY</bcp14> generate an
+   immediate <x:ref>100 (Continue)</x:ref> response to encourage the client to
+   begin sending the content.
+</t>
+</section>
+
+<section title="From" anchor="field.from">
+  <x:anchor-alias value="header.from"/>
+  <iref primary="true" item="Fields" subitem="From" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="From" x:for-anchor=""/><iref primary="true" item="From header field" x:for-anchor=""/>
+  <x:anchor-alias value="From"/>
+  <x:anchor-alias value="mailbox"/>
+<t>
+   The "From" header field contains an Internet email address for a human
+   user who controls the requesting user agent. The address ought to be
+   machine-usable, as defined by "mailbox"
+   in <xref x:sec="3.4" x:fmt="of" target="RFC5322"/>:
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="From"/>
+  <x:ref>From</x:ref>    = <x:ref>mailbox</x:ref>
+
+  <x:ref>mailbox</x:ref> = &lt;mailbox, see <xref x:sec="3.4" x:fmt="," target="RFC5322"/>&gt;
+</sourcecode>
+<t>
+   An example is:
+</t>
+<sourcecode type="http-message">
+From: spider-admin@example.org
+</sourcecode>
+<t>
+   The From header field is rarely sent by non-robotic user agents.
+   A user agent <bcp14>SHOULD NOT</bcp14> send a From header field without explicit
+   configuration by the user, since that might conflict with the user's
+   privacy interests or their site's security policy.
+</t>
+<t>
+   A robotic user agent <bcp14>SHOULD</bcp14> send a valid From header field so that the
+   person responsible for running the robot can be contacted if problems
+   occur on servers, such as if the robot is sending excessive, unwanted,
+   or invalid requests.
+</t>
+<t>
+   A server <bcp14>SHOULD NOT</bcp14> use the From header field for access control or
+   authentication, since its value is expected to be visible to anyone
+   receiving or observing the request and is often recorded within logfiles
+   and error reports without any expectation of privacy.
+</t>
+</section>
+
+<section title="Referer" anchor="field.referer">
+  <x:anchor-alias value="header.referer"/>
+  <iref primary="true" item="Fields" subitem="Referer" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Referer" x:for-anchor=""/><iref primary="true" item="Referer header field" x:for-anchor=""/>
+  <x:anchor-alias value="Referer"/>
+<t>
+   The "Referer" [sic] header field allows the user agent to specify a URI
+   reference for the resource from which the <x:ref>target URI</x:ref> was
+   obtained (i.e., the "referrer", though the field name is misspelled).
+   A user agent <bcp14>MUST NOT</bcp14> include the fragment and userinfo components
+   of the URI reference <xref target="URI"/>, if any, when generating the
+   Referer field value.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Referer"/>
+  <x:ref>Referer</x:ref> = <x:ref>absolute-URI</x:ref> / <x:ref>partial-URI</x:ref>
+</sourcecode>
+<t>
+   The field value is either an <x:ref>absolute-URI</x:ref> or a
+   <x:ref>partial-URI</x:ref>. In the latter case (<xref target="uri"/>),
+   the referenced URI is relative to the target URI
+   (<xref target="URI" x:fmt="," x:sec="5"/>).
+</t>
+<t>
+   The Referer header field allows servers to generate back-links to other
+   resources for simple analytics, logging, optimized caching, etc. It also
+   allows obsolete or mistyped links to be found for maintenance. Some servers
+   use the Referer header field as a means of denying links from other sites
+   (so-called "deep linking") or restricting cross-site request forgery (CSRF),
+   but not all requests contain it.
+</t>
+<t>
+   Example:
+</t>
+<sourcecode type="http-message">
+Referer: http://www.example.org/hypertext/Overview.html
+</sourcecode>
+<t>
+   If the target URI was obtained from a source that does not have its own
+   URI (e.g., input from the user keyboard, or an entry within the user's
+   bookmarks/favorites), the user agent <bcp14>MUST</bcp14> either exclude the Referer header field
+   or send it with a value of "about:blank".
+</t>
+<t>
+   The Referer header field value need not convey the full URI of the referring
+   resource; a user agent <bcp14>MAY</bcp14> truncate parts other than the referring origin.
+</t>
+<t>
+   The Referer header field has the potential to reveal information about the request
+   context or browsing history of the user, which is a privacy concern if the
+   referring resource's identifier reveals personal information (such as an
+   account name) or a resource that is supposed to be confidential (such as
+   behind a firewall or internal to a secured service). Most general-purpose
+   user agents do not send the Referer header field when the referring
+   resource is a local "file" or "data" URI. A user agent <bcp14>SHOULD NOT</bcp14> send a
+   <x:ref>Referer</x:ref> header field if the referring resource was accessed with
+   a secure protocol and the request target has an origin differing from that
+   of the referring resource, unless the referring resource explicitly allows
+   Referer to be sent. A user agent <bcp14>MUST NOT</bcp14> send a
+   <x:ref>Referer</x:ref> header field in an unsecured HTTP request if the
+   referring resource was accessed with a secure protocol.
+   See <xref target="sensitive.information.in.uris"/> for additional
+   security considerations.
+</t>
+<t>
+   Some intermediaries have been known to indiscriminately remove Referer
+   header fields from outgoing requests. This has the unfortunate side effect
+   of interfering with protection against CSRF attacks, which can be far
+   more harmful to their users. Intermediaries and user agent extensions that
+   wish to limit information disclosure in Referer ought to restrict their
+   changes to specific edits, such as replacing internal domain names with
+   pseudonyms or truncating the query and/or path components.
+   An intermediary <bcp14>SHOULD NOT</bcp14> modify or delete the Referer header field when
+   the field value shares the same scheme and host as the target URI.
+</t>
+</section>
+
+<section title="TE" anchor="field.te">
+  <x:anchor-alias value="header.te"/>
+  <iref primary="true" item="Fields" subitem="TE" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="TE" x:for-anchor=""/><iref primary="true" item="TE header field" x:for-anchor=""/>
+  <x:anchor-alias value="TE"/>
+  <x:anchor-alias value="t-codings"/>
+  <x:anchor-alias value="transfer-coding"/>
+  <x:anchor-alias value="transfer-parameter"/>
+<t>
+   The "TE" header field describes capabilities of the client with regard to
+   transfer codings and trailer sections.
+</t>
+<t>
+   As described in <xref target="trailer.fields"/>, a TE field with a
+   "trailers" member sent in a request indicates that the client will not
+   discard trailer fields.
+</t>
+<t>
+   TE is also used within HTTP/1.1 to advise servers about which transfer
+   codings the client is able to accept in a response.
+   As of publication, only HTTP/1.1 uses transfer codings
+   (see <xref target="HTTP11" x:rel="#transfer.codings"/>).
+</t>
+<t>
+   The TE field value is a list of members, with each member (aside from
+   "trailers") consisting of a transfer coding name token with an optional
+   weight indicating the client's relative preference for that
+   transfer coding (<xref target="quality.values"/>) and
+   optional parameters for that transfer coding.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="TE"/><iref primary="true" item="Grammar" subitem="t-codings"/><iref primary="true" item="Grammar" subitem="transfer-coding"/><iref primary="true" item="Grammar" subitem="transfer-parameter"/>
+  <x:ref>TE</x:ref>                 = #<x:ref>t-codings</x:ref>
+  <x:ref>t-codings</x:ref>          = "trailers" / ( <x:ref>transfer-coding</x:ref> [ <x:ref>weight</x:ref> ] )
+  <x:ref>transfer-coding</x:ref>    = <x:ref>token</x:ref> *( <x:ref>OWS</x:ref> ";" <x:ref>OWS</x:ref> <x:ref>transfer-parameter</x:ref> )
+  <x:ref>transfer-parameter</x:ref> = <x:ref>token</x:ref> <x:ref>BWS</x:ref> "=" <x:ref>BWS</x:ref> ( <x:ref>token</x:ref> / <x:ref>quoted-string</x:ref> )
+</sourcecode>
+<t>
+   A sender of TE <bcp14>MUST</bcp14> also send a "TE" connection option within the
+   <x:ref>Connection</x:ref> header field (<xref target="field.connection"/>)
+   to inform intermediaries not to forward this field.
+</t>
+</section>
+
+<section title="User-Agent" anchor="field.user-agent">
+  <x:anchor-alias value="header.user-agent"/>
+  <iref primary="true" item="Fields" subitem="User-Agent" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="User-Agent" x:for-anchor=""/><iref primary="true" item="User-Agent header field" x:for-anchor=""/>
+  <x:anchor-alias value="User-Agent"/>
+  <x:anchor-alias value="product"/>
+  <x:anchor-alias value="product-version"/>
+<t>
+   The "User-Agent" header field contains information about the user agent
+   originating the request, which is often used by servers to help identify
+   the scope of reported interoperability problems, to work around or tailor
+   responses to avoid particular user agent limitations, and for analytics
+   regarding browser or operating system use. A user agent <bcp14>SHOULD</bcp14> send
+   a User-Agent header field in each request unless specifically configured not
+   to do so.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="User-Agent"/>
+  <x:ref>User-Agent</x:ref> = <x:ref>product</x:ref> *( <x:ref>RWS</x:ref> ( <x:ref>product</x:ref> / <x:ref>comment</x:ref> ) )
+</sourcecode>
+<t>
+   The User-Agent field value consists of one or more product identifiers,
+   each followed by zero or more comments (<xref target="comments"/>), which together
+   identify the user agent software and its significant subproducts.
+   By convention, the product identifiers are listed in decreasing order of
+   their significance for identifying the user agent software. Each product
+   identifier consists of a name and optional version.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="product"/><iref primary="true" item="Grammar" subitem="product-version"/>
+  <x:ref>product</x:ref>         = <x:ref>token</x:ref> ["/" <x:ref>product-version</x:ref>]
+  <x:ref>product-version</x:ref> = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   A sender <bcp14>SHOULD</bcp14> limit generated product identifiers to what is necessary
+   to identify the product; a sender <bcp14>MUST NOT</bcp14> generate advertising or other
+   nonessential information within the product identifier.
+   A sender <bcp14>SHOULD NOT</bcp14> generate information in <x:ref>product-version</x:ref>
+   that is not a version identifier (i.e., successive versions of the same
+   product name ought to differ only in the product-version portion of the
+   product identifier).
+</t>
+<t>
+   Example:
+</t>
+<sourcecode type="http-message">
+User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+</sourcecode>
+<t>
+   A user agent <bcp14>SHOULD NOT</bcp14> generate a User-Agent header field containing needlessly
+   fine-grained detail and <bcp14>SHOULD</bcp14> limit the addition of subproducts by third
+   parties. Overly long and detailed User-Agent field values increase request
+   latency and the risk of a user being identified against their wishes
+   ("fingerprinting").
+</t>
+<t>
+   Likewise, implementations are encouraged not to use the product tokens of
+   other implementations in order to declare compatibility with them, as this
+   circumvents the purpose of the field. If a user agent masquerades as a
+   different user agent, recipients can assume that the user intentionally
+   desires to see responses tailored for that identified user agent, even
+   if they might not work as well for the actual user agent being used.
+</t>
+</section>
+</section>
+
+<section title="Response Context Fields" anchor="response.context">
+  <x:anchor-alias value="response-header"/>
+<t>
+   The response header fields below provide additional information about the
+   response, beyond what is implied by the status code, including information
+   about the server, about the <x:ref>target resource</x:ref>, or about related
+   resources.
+</t>
+
+<section title="Allow" anchor="field.allow">
+  <x:anchor-alias value="header.allow"/>
+  <iref primary="true" item="Fields" subitem="Allow" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Allow" x:for-anchor=""/><iref primary="true" item="Allow header field" x:for-anchor=""/>
+  <x:anchor-alias value="Allow"/>
+<t>
+   The "Allow" header field lists the set of methods advertised as
+   supported by the <x:ref>target resource</x:ref>. The purpose of this field
+   is strictly to inform the recipient of valid request methods associated
+   with the resource.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Allow"/>
+  <x:ref>Allow</x:ref> = #<x:ref>method</x:ref>
+</sourcecode>
+<t>
+   Example of use:
+</t>
+<sourcecode type="http-message">
+Allow: GET, HEAD, PUT
+</sourcecode>
+<t>
+   The actual set of allowed methods is defined by the origin server at the
+   time of each request. An origin server <bcp14>MUST</bcp14> generate an Allow header field in a
+   <x:ref>405 (Method Not Allowed)</x:ref> response and <bcp14>MAY</bcp14> do so in any
+   other response. An empty Allow field value indicates that the resource
+   allows no methods, which might occur in a 405 response if the resource has
+   been temporarily disabled by configuration.
+</t>
+<t>
+   A proxy <bcp14>MUST NOT</bcp14> modify the Allow header field — it does not need
+   to understand all of the indicated methods in order to handle them
+   according to the generic message handling rules.
+</t>
+</section>
+
+<section title="Location" anchor="field.location">
+  <x:anchor-alias value="header.location"/>
+  <iref primary="true" item="Fields" subitem="Location" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Location" x:for-anchor=""/><iref primary="true" item="Location header field" x:for-anchor=""/>
+  <x:anchor-alias value="Location"/>
+<t>
+   The "Location" header field is used in some responses to refer to a
+   specific resource in relation to the response. The type of relationship is
+   defined by the combination of request method and status code semantics.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Location"/>
+  <x:ref>Location</x:ref> = <x:ref>URI-reference</x:ref>
+</sourcecode>
+<t>
+   The field value consists of a single URI-reference. When it has the form
+   of a relative reference (<xref target="URI" x:fmt="," x:sec="4.2"/>),
+   the final value is computed by resolving it against the target
+   URI (<xref target="URI" x:fmt="," x:sec="5"/>).
+</t>
+<t>
+   For <x:ref>201 (Created)</x:ref> responses, the Location value refers to
+   the primary resource created by the request.
+   For <x:ref>3xx (Redirection)</x:ref> responses, the Location value refers
+   to the preferred target resource for automatically redirecting the request.
+</t>
+<t>
+   If the Location value provided in a <x:ref>3xx (Redirection)</x:ref>
+   response does not have a fragment component, a user agent <bcp14>MUST</bcp14> process the
+   redirection as if the value inherits the fragment component of the URI
+   reference used to generate the target URI (i.e., the redirection
+   inherits the original reference's fragment, if any).
+</t>
+<t>
+   For example, a GET request generated for the URI reference
+   "http://www.example.org/~tim" might result in a
+   <x:ref>303 (See Other)</x:ref> response containing the header field:
+</t>
+<sourcecode type="http-message">
+Location: /People.html#tim
+</sourcecode>
+<t>
+   which suggests that the user agent redirect to
+   "http://www.example.org/People.html#tim"
+</t>
+<t>
+   Likewise, a GET request generated for the URI reference
+   "http://www.example.org/index.html#larry" might result in a
+   <x:ref>301 (Moved Permanently)</x:ref> response containing the header
+   field:
+</t>
+<sourcecode type="http-message">
+Location: http://www.example.net/index.html
+</sourcecode>
+<t>
+   which suggests that the user agent redirect to
+   "http://www.example.net/index.html#larry", preserving the original fragment
+   identifier.
+</t>
+<t>
+   There are circumstances in which a fragment identifier in a Location
+   value would not be appropriate. For example, the Location header field in a
+   <x:ref>201 (Created)</x:ref> response is supposed to provide a URI that is
+   specific to the created resource.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Some recipients attempt to recover from Location header fields
+    that are not valid URI references. This specification does not mandate or
+    define such processing, but does allow it for the sake of robustness.
+    A Location field value cannot allow a list of members because the comma list separator
+    is a valid data character within a URI-reference. If an invalid message is sent with multiple
+    Location field lines, a recipient along the path might combine those field lines into
+    one value. Recovery of a valid Location field value from that situation is difficult and not
+    interoperable across implementations.
+  </t>
+</aside>
+<aside>
+  <t>
+    <x:h>Note:</x:h> The <x:ref>Content-Location</x:ref> header field
+    (<xref target="field.content-location"/>) differs from Location in that the
+    Content-Location refers to the most specific resource corresponding to the
+    enclosed representation. It is therefore possible for a response to contain
+    both the Location and Content-Location header fields.
+  </t>
+</aside>
+</section>
+
+<section title="Retry-After" anchor="field.retry-after">
+  <x:anchor-alias value="header.retry-after"/>
+  <iref primary="true" item="Fields" subitem="Retry-After" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Retry-After" x:for-anchor=""/><iref primary="true" item="Retry-After header field" x:for-anchor=""/>
+  <x:anchor-alias value="Retry-After"/>
+<t>
+   Servers send the "Retry-After" header field to indicate how long the user
+   agent ought to wait before making a follow-up request. When sent with a
+   <x:ref>503 (Service Unavailable)</x:ref> response, Retry-After indicates
+   how long the service is expected to be unavailable to the client.
+   When sent with any <x:ref>3xx (Redirection)</x:ref> response, Retry-After
+   indicates the minimum time that the user agent is asked to wait before
+   issuing the redirected request.
+</t>
+<t>
+   The Retry-After field value can be either an HTTP-date or a number
+   of seconds to delay after receiving the response.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Retry-After"/>
+  <x:ref>Retry-After</x:ref> = <x:ref>HTTP-date</x:ref> / <x:ref>delay-seconds</x:ref>
+</sourcecode>
+<t anchor="rule.delay-seconds">
+  <x:anchor-alias value="delay-seconds"/>
+   A delay-seconds value is a non-negative decimal integer, representing time
+   in seconds.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="delay-seconds"/>
+  <x:ref>delay-seconds</x:ref>  = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   Two examples of its use are
+</t>
+<sourcecode type="http-message">
+Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
+Retry-After: 120
+</sourcecode>
+<t>
+   In the latter example, the delay is 2 minutes.
+</t>
+</section>
+
+<section title="Server" anchor="field.server">
+  <x:anchor-alias value="header.server"/>
+  <iref primary="true" item="Fields" subitem="Server" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Server" x:for-anchor=""/><iref primary="true" item="Server header field" x:for-anchor=""/>
+  <x:anchor-alias value="Server"/>
+<t>
+   The "Server" header field contains information about the
+   software used by the origin server to handle the request, which is often
+   used by clients to help identify the scope of reported interoperability
+   problems, to work around or tailor requests to avoid particular server
+   limitations, and for analytics regarding server or operating system use.
+   An origin server <bcp14>MAY</bcp14> generate a Server header field in its responses.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Server"/>
+  <x:ref>Server</x:ref> = <x:ref>product</x:ref> *( <x:ref>RWS</x:ref> ( <x:ref>product</x:ref> / <x:ref>comment</x:ref> ) )
+</sourcecode>
+<t>
+   The Server header field value consists of one or more product identifiers, each
+   followed by zero or more comments (<xref target="comments"/>), which together
+   identify the origin server software and its significant subproducts.
+   By convention, the product identifiers are listed in decreasing order of
+   their significance for identifying the origin server software. Each product
+   identifier consists of a name and optional version, as defined in
+   <xref target="field.user-agent"/>.
+</t>
+<t>
+   Example:
+</t>
+<sourcecode type="http-message">
+Server: CERN/3.0 libwww/2.17
+</sourcecode>
+<t>
+   An origin server <bcp14>SHOULD NOT</bcp14> generate a Server header field containing needlessly
+   fine-grained detail and <bcp14>SHOULD</bcp14> limit the addition of subproducts by third
+   parties. Overly long and detailed Server field values increase response
+   latency and potentially reveal internal implementation details that might
+   make it (slightly) easier for attackers to find and exploit known security
+   holes.
+</t>
+</section>
+</section>
+</section>
+
+<section title="HTTP Authentication" anchor="authentication">
+<section title="Authentication Scheme" anchor="auth.scheme">
+  <x:anchor-alias value="auth-scheme"/>
+<t>
+   HTTP provides a general framework for access control and authentication,
+   via an extensible set of challenge-response authentication schemes, which
+   can be used by a server to challenge a client request and by a client to
+   provide authentication information. It uses a case-insensitive
+   token to identify the authentication scheme:
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="auth-scheme"/>
+  auth-scheme    = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   Aside from the general framework, this document does not specify any
+   authentication schemes. New and existing authentication schemes are
+   specified independently and ought to be registered within the
+   "Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry".
+   For example, the "basic" and "digest" authentication schemes are defined by
+   <xref target="RFC7617"/> and <xref target="RFC7616"/>, respectively.
+</t>
+</section>
+
+<section title="Authentication Parameters" anchor="auth.params">
+  <x:anchor-alias value="auth-param"/>
+  <x:anchor-alias value="token68"/>
+<t>
+   The authentication scheme is followed by additional information necessary
+   for achieving authentication via that scheme as either a
+   comma-separated list of parameters or a single sequence of characters
+   capable of holding base64-encoded information.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="token68"/>
+  token68        = 1*( <x:ref>ALPHA</x:ref> / <x:ref>DIGIT</x:ref> /
+                       "-" / "." / "_" / "~" / "+" / "/" ) *"="
+</sourcecode>
+<t>
+   The token68 syntax allows the 66 unreserved URI characters
+   (<xref target="URI"/>), plus a few others, so that it can hold a
+   base64, base64url (URL and filename safe alphabet), base32, or base16 (hex)
+   encoding, with or without padding, but excluding whitespace
+   (<xref target="RFC4648"/>).
+</t>
+<t>
+   Authentication parameters are name/value pairs, where the name token is
+   matched case-insensitively
+   and each parameter name <bcp14>MUST</bcp14> only occur once per challenge.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="auth-param"/>
+  auth-param     = <x:ref>token</x:ref> <x:ref>BWS</x:ref> "=" <x:ref>BWS</x:ref> ( <x:ref>token</x:ref> / <x:ref>quoted-string</x:ref> )
+</sourcecode>
+<t>
+   Parameter values can be expressed either as "token" or as "quoted-string"
+   (<xref target="fields.components"/>).
+   Authentication scheme definitions need to accept both notations, both for
+   senders and recipients, to allow recipients to use generic parsing
+   components regardless of the authentication scheme.
+</t>
+<t>
+   For backwards compatibility, authentication scheme definitions can restrict
+   the format for senders to one of the two variants. This can be important
+   when it is known that deployed implementations will fail when encountering
+   one of the two formats.
+</t>
+</section>
+
+<section title="Challenge and Response" anchor="challenge.and.response">
+  <x:anchor-alias value="challenge"/>
+<t>
+   A <x:ref>401 (Unauthorized)</x:ref> response message is used by an origin
+   server to challenge the authorization of a user agent, including a
+   <x:ref>WWW-Authenticate</x:ref> header field containing at least one
+   challenge applicable to the requested resource.
+</t>
+<t>
+   A <x:ref>407 (Proxy Authentication Required)</x:ref> response message is
+   used by a proxy to challenge the authorization of a client, including a
+   <x:ref>Proxy-Authenticate</x:ref> header field containing at least one
+   challenge applicable to the proxy for the requested resource.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="challenge"/>
+  <x:ref>challenge</x:ref>   = <x:ref>auth-scheme</x:ref> [ 1*<x:ref>SP</x:ref> ( <x:ref>token68</x:ref> / #<x:ref>auth-param</x:ref> ) ]
+</sourcecode>
+<aside>
+  <t>
+     <x:h>Note:</x:h> Many clients fail to parse a challenge that contains an unknown
+     scheme. A workaround for this problem is to list well-supported schemes
+     (such as "basic") first.<!-- see https://greenbytes.de/tech/tc/httpauth/#multibasicunknown2 -->
+  </t>
+</aside>
+<t>
+   A user agent that wishes to authenticate itself with an origin server
+   — usually, but not necessarily, after receiving a
+   <x:ref>401 (Unauthorized)</x:ref> — can do so by including an
+   <x:ref>Authorization</x:ref> header field with the request.
+</t>
+<t>
+   A client that wishes to authenticate itself with a proxy — usually,
+   but not necessarily, after receiving a
+   <x:ref>407 (Proxy Authentication Required)</x:ref> — can do so by
+   including a <x:ref>Proxy-Authorization</x:ref> header field with the
+   request.
+</t>
+</section>
+
+<section title="Credentials" anchor="credentials">
+<t>
+   Both the <x:ref>Authorization</x:ref> field value and the
+   <x:ref>Proxy-Authorization</x:ref> field value contain the client's
+   credentials for the realm of the resource being requested, based upon a
+   challenge received in a response (possibly at some point in the past).
+   When creating their values, the user agent ought to do so by selecting the
+   challenge with what it considers to be the most secure auth-scheme that it
+   understands, obtaining credentials from the user as appropriate.
+   Transmission of credentials within header field values implies significant
+   security considerations regarding the confidentiality of the underlying
+   connection, as described in
+   <xref target="confidentiality.of.credentials"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="credentials"/>
+  <x:ref>credentials</x:ref> = <x:ref>auth-scheme</x:ref> [ 1*<x:ref>SP</x:ref> ( <x:ref>token68</x:ref> / #<x:ref>auth-param</x:ref> ) ]
+</sourcecode>
+<t>
+   Upon receipt of a request for a protected resource that omits credentials,
+   contains invalid credentials (e.g., a bad password) or partial credentials
+   (e.g., when the authentication scheme requires more than one round trip),
+   an origin server <bcp14>SHOULD</bcp14> send a <x:ref>401 (Unauthorized)</x:ref> response
+   that contains a <x:ref>WWW-Authenticate</x:ref> header field with at least
+   one (possibly new) challenge applicable to the requested resource.
+</t>
+<t>
+   Likewise, upon receipt of a request that omits proxy credentials or
+   contains invalid or partial proxy credentials, a proxy that requires
+   authentication <bcp14>SHOULD</bcp14> generate a
+   <x:ref>407 (Proxy Authentication Required)</x:ref> response that contains
+   a <x:ref>Proxy-Authenticate</x:ref> header field with at least one
+   (possibly new) challenge applicable to the proxy.
+</t>
+<t>
+   A server that receives valid credentials that are not adequate to gain
+   access ought to respond with the <x:ref>403 (Forbidden)</x:ref> status
+   code (<xref target="status.403"/>).
+</t>
+<t>
+   HTTP does not restrict applications to this simple challenge-response
+   framework for access authentication. Additional mechanisms can be used,
+   such as authentication at the transport level or via message encapsulation,
+   and with additional header fields specifying authentication information.
+   However, such additional mechanisms are not defined by this specification.
+</t>
+<t>
+   Note that various custom mechanisms for user authentication use the
+   Set-Cookie and Cookie header fields, defined in <xref target="COOKIE"/>,
+   for passing tokens related to authentication.
+</t>
+</section>
+
+<section title="Establishing a Protection Space (Realm)" anchor="protection.space">
+  <iref item="Protection Space"/>
+  <iref item="Realm"/>
+  <iref item="origin"/>
+<t>
+   The <x:dfn>realm</x:dfn> authentication parameter is reserved for use by
+   authentication schemes that wish to indicate a scope of protection.
+</t>
+<t>
+   A <x:dfn>protection space</x:dfn> is defined by the origin (see
+   <xref target="origin"/>) of the
+   server being accessed, in combination with the realm value if present.
+   These realms allow the protected resources on a server to be
+   partitioned into a set of protection spaces, each with its own
+   authentication scheme and/or authorization database. The realm value
+   is a string, generally assigned by the origin server, that can have
+   additional semantics specific to the authentication scheme. Note that a
+   response can have multiple challenges with the same auth-scheme but
+   with different realms.
+</t>
+<t>
+   The protection space determines the domain over which credentials can
+   be automatically applied. If a prior request has been authorized, the
+   user agent <bcp14>MAY</bcp14> reuse the same credentials for all other requests within
+   that protection space for a period of time determined by the authentication
+   scheme, parameters, and/or user preferences (such as a configurable
+   inactivity timeout).
+</t>
+<t>
+   The extent of a protection space, and therefore the requests to which
+   credentials might be automatically applied, is not necessarily known to
+   clients without additional information. An authentication scheme might
+   define parameters that describe the extent of a protection space. Unless
+   specifically allowed by the authentication scheme, a single protection
+   space cannot extend outside the scope of its server.
+</t>
+<t>
+   For historical reasons, a sender <bcp14>MUST</bcp14> only generate the quoted-string syntax.
+   Recipients might have to support both token and quoted-string syntax for
+   maximum interoperability with existing clients that have been accepting both
+   notations for a long time.
+</t>
+</section>
+
+<section title="Authenticating Users to Origin Servers" anchor="auth.user.origin.server">
+
+<section title="WWW-Authenticate" anchor="field.www-authenticate">
+  <x:anchor-alias value="header.www-authenticate"/>
+  <iref primary="true" item="Fields" subitem="WWW-Authenticate" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="WWW-Authenticate" x:for-anchor=""/><iref primary="true" item="WWW-Authenticate header field" x:for-anchor=""/>
+  <x:anchor-alias value="WWW-Authenticate"/>
+<t>
+   The "WWW-Authenticate" response header field indicates the authentication
+   scheme(s) and parameters applicable to the target resource.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="WWW-Authenticate"/>
+  <x:ref>WWW-Authenticate</x:ref> = #<x:ref>challenge</x:ref>
+</sourcecode>
+<t>
+   A server generating a <x:ref>401 (Unauthorized)</x:ref> response
+   <bcp14>MUST</bcp14> send a WWW-Authenticate header field containing at least one
+   challenge.  A server <bcp14>MAY</bcp14> generate a WWW-Authenticate header field
+   in other response messages to indicate that supplying credentials
+   (or different credentials) might affect the response.
+</t>
+<t>
+   A proxy forwarding a response <bcp14>MUST NOT</bcp14> modify any
+   <x:ref>WWW-Authenticate</x:ref> header fields in that response.
+</t>
+<t>
+   User agents are advised to take special care in parsing the field value, as
+   it might contain more than one challenge, and each challenge can contain a
+   comma-separated list of authentication parameters. Furthermore, the header
+   field itself can occur multiple times.
+</t>
+<t>
+  For instance:
+</t>
+<sourcecode type="http-message">
+WWW-Authenticate: Basic realm="simple", Newauth realm="apps",
+                 type=1, title="Login to \"apps\""
+</sourcecode>
+<t>
+  This header field contains two challenges, one for the "Basic" scheme with
+  a realm value of "simple" and another for the "Newauth" scheme with a
+  realm value of "apps". It also contains two additional parameters, "type"
+  and "title".
+</t>
+<t>
+  Some user agents do not recognize this form, however. As a result, sending
+  a WWW-Authenticate field value with more than one member on the same field
+  line might not be interoperable.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> The challenge grammar production uses the list syntax as
+    well. Therefore, a sequence of comma, whitespace, and comma can be
+    considered either as applying to the preceding challenge, or to be an
+    empty entry in the list of challenges. In practice, this ambiguity
+    does not affect the semantics of the header field value and thus is
+    harmless.
+  </t>
+</aside>
+</section>
+
+<section title="Authorization" anchor="field.authorization">
+  <x:anchor-alias value="header.authorization"/>
+  <iref primary="true" item="Fields" subitem="Authorization" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Authorization" x:for-anchor=""/><iref primary="true" item="Authorization header field" x:for-anchor=""/>
+  <x:anchor-alias value="Authorization"/>
+<t>
+   The "Authorization" header field allows a user agent to authenticate itself
+   with an origin server — usually, but not necessarily, after receiving
+   a <x:ref>401 (Unauthorized)</x:ref> response. Its value consists of
+   credentials containing the authentication information of the user agent for
+   the realm of the resource being requested.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Authorization"/>
+  <x:ref>Authorization</x:ref> = <x:ref>credentials</x:ref>
+</sourcecode>
+<t>
+   If a request is authenticated and a realm specified, the same credentials
+   are presumed to be valid for all other requests within this realm (assuming
+   that the authentication scheme itself does not require otherwise, such as
+   credentials that vary according to a challenge value or using synchronized
+   clocks).
+</t>
+<t>
+   A proxy forwarding a request <bcp14>MUST NOT</bcp14> modify any
+   <x:ref>Authorization</x:ref> header fields in that request.
+   See <xref target="CACHING" x:rel="#caching.authenticated.responses"/> for details of and requirements
+   pertaining to handling of the Authorization header field by HTTP caches.
+</t>
+</section>
+
+<section title="Authentication-Info" anchor="field.authentication-info">
+  <x:anchor-alias value="header.authentication-info"/>
+  <iref primary="true" item="Fields" subitem="Authentication-Info" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Authentication-Info" x:for-anchor=""/><iref primary="true" item="Authentication-Info header field" x:for-anchor=""/>
+  <x:anchor-alias value="Authentication-Info"/>
+<t>
+   HTTP authentication schemes can use the "Authentication-Info" response
+   field to communicate information after the client's authentication credentials have been accepted.
+   This information can include a finalization message from the server (e.g., it can contain the
+   server authentication).
+</t>
+<t>
+   The field value is a list of parameters (name/value pairs), using the "auth-param"
+   syntax defined in <xref target="challenge.and.response"/>.
+   This specification only describes the generic format; authentication schemes
+   using Authentication-Info will define the individual parameters. The "Digest"
+   Authentication Scheme, for instance, defines multiple parameters in
+   <xref target="RFC7616" x:sec="3.5"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Authentication-Info"/>
+  <x:ref>Authentication-Info</x:ref> = #<x:ref>auth-param</x:ref>
+</sourcecode>
+<t>
+   The Authentication-Info field can be used in any HTTP response,
+   independently of request method and status code. Its semantics are defined
+   by the authentication scheme indicated by the <x:ref>Authorization</x:ref> header field
+   (<xref target="field.authorization"/>) of the corresponding request.
+</t>
+<t>
+   A proxy forwarding a response is not allowed to modify the field value in any
+   way.
+</t>
+<t>
+   Authentication-Info can be sent as a trailer field
+   (<xref target="trailer.fields"/>)
+   when the authentication scheme explicitly allows this.
+</t>
+</section>
+</section>
+
+<section title="Authenticating Clients to Proxies" anchor="auth.client.proxy">
+
+<section title="Proxy-Authenticate" anchor="field.proxy-authenticate">
+  <x:anchor-alias value="header.proxy-authenticate"/>
+  <iref primary="true" item="Fields" subitem="Proxy-Authenticate" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Proxy-Authenticate" x:for-anchor=""/><iref primary="true" item="Proxy-Authenticate header field" x:for-anchor=""/>
+  <x:anchor-alias value="Proxy-Authenticate"/>
+<t>
+   The "Proxy-Authenticate" header field consists of at least one
+   challenge that indicates the authentication scheme(s) and parameters
+   applicable to the proxy for this request.
+   A proxy <bcp14>MUST</bcp14> send at least one Proxy-Authenticate header field in
+   each <x:ref>407 (Proxy Authentication Required)</x:ref> response that it
+   generates.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Proxy-Authenticate"/>
+  <x:ref>Proxy-Authenticate</x:ref> = #<x:ref>challenge</x:ref>
+</sourcecode>
+<t>
+   Unlike <x:ref>WWW-Authenticate</x:ref>, the Proxy-Authenticate header field
+   applies only to the next outbound client on the response chain.
+   This is because only the client that chose a given proxy is likely to have
+   the credentials necessary for authentication.  However, when multiple
+   proxies are used within the same administrative domain, such as office and
+   regional caching proxies within a large corporate network, it is common
+   for credentials to be generated by the user agent and passed through the
+   hierarchy until consumed.  Hence, in such a configuration, it will appear
+   as if Proxy-Authenticate is being forwarded because each proxy will send
+   the same challenge set.
+</t>
+<t>
+   Note that the parsing considerations for <x:ref>WWW-Authenticate</x:ref>
+   apply to this header field as well; see <xref target="field.www-authenticate"/>
+   for details.
+</t>
+</section>
+
+<section title="Proxy-Authorization" anchor="field.proxy-authorization">
+  <x:anchor-alias value="header.proxy-authorization"/>
+  <iref primary="true" item="Fields" subitem="Proxy-Authorization" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Proxy-Authorization" x:for-anchor=""/><iref primary="true" item="Proxy-Authorization header field" x:for-anchor=""/>
+  <x:anchor-alias value="Proxy-Authorization"/>
+<t>
+   The "Proxy-Authorization" header field allows the client to
+   identify itself (or its user) to a proxy that requires
+   authentication. Its value consists of credentials containing the
+   authentication information of the client for the proxy and/or realm of the
+   resource being requested.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Proxy-Authorization"/>
+  <x:ref>Proxy-Authorization</x:ref> = <x:ref>credentials</x:ref>
+</sourcecode>
+<t>
+   Unlike <x:ref>Authorization</x:ref>, the Proxy-Authorization header field
+   applies only to the next inbound proxy that demanded authentication using
+   the <x:ref>Proxy-Authenticate</x:ref> header field. When multiple proxies are used
+   in a chain, the Proxy-Authorization header field is consumed by the first
+   inbound proxy that was expecting to receive credentials. A proxy <bcp14>MAY</bcp14>
+   relay the credentials from the client request to the next proxy if that is
+   the mechanism by which the proxies cooperatively authenticate a given
+   request.
+</t>
+</section>
+
+<section title="Proxy-Authentication-Info" anchor="field.proxy-authentication-info">
+  <x:anchor-alias value="header.proxy-authentication-info"/>
+  <iref primary="true" item="Fields" subitem="Proxy-Authentication-Info" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Proxy-Authentication-Info" x:for-anchor=""/><iref primary="true" item="Proxy-Authentication-Info header field" x:for-anchor=""/>
+  <x:anchor-alias value="Proxy-Authentication-Info"/>
+<t>
+   The "Proxy-Authentication-Info" response header field is equivalent to
+   <x:ref>Authentication-Info</x:ref>, except that it applies to proxy authentication (<xref target="challenge.and.response"/>)
+   and its semantics are defined by the
+   authentication scheme indicated by the Proxy-Authorization header field
+   (<xref target="field.proxy-authorization"/>)
+   of the corresponding request:
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Proxy-Authentication-Info"/>
+  <x:ref>Proxy-Authentication-Info</x:ref> = #<x:ref>auth-param</x:ref>
+</sourcecode>
+<t>
+   However, unlike <x:ref>Authentication-Info</x:ref>, the Proxy-Authentication-Info header
+   field applies only to the next outbound client on the response chain. This is
+   because only the client that chose a given proxy is likely to have the
+   credentials necessary for authentication. However, when multiple proxies are
+   used within the same administrative domain, such as office and regional
+   caching proxies within a large corporate network, it is common for
+   credentials to be generated by the user agent and passed through the
+   hierarchy until consumed. Hence, in such a configuration, it will appear as
+   if Proxy-Authentication-Info is being forwarded because each proxy will send
+   the same field value.
+</t>
+<t>
+   Proxy-Authentication-Info can be sent as a trailer field
+   (<xref target="trailer.fields"/>)
+   when the authentication scheme explicitly allows this.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Content Negotiation" anchor="content.negotiation">
+  <x:anchor-alias value="content negotiation"/>
+<t>
+   When responses convey content, whether indicating a success or
+   an error, the origin server often has different ways of representing that
+   information; for example, in different formats, languages, or encodings.
+   Likewise, different users or user agents might have differing capabilities,
+   characteristics, or preferences that could influence which representation,
+   among those available, would be best to deliver. For this reason, HTTP
+   provides mechanisms for <x:ref>content negotiation</x:ref>.
+</t>
+<t>
+   This specification defines three patterns of content negotiation that can
+   be made visible within the protocol:
+   "proactive" negotiation, where the server selects the representation based
+   upon the user agent's stated preferences; "reactive" negotiation,
+   where the server provides a list of representations for the user agent to
+   choose from; and "request content" negotiation, where the user agent
+   selects the representation for a future request based upon the server's
+   stated preferences in past responses.
+</t>
+<t>
+   Other patterns of content negotiation include
+   "conditional content", where the representation consists of multiple
+   parts that are selectively rendered based on user agent parameters,
+   "active content", where the representation contains a script that
+   makes additional (more specific) requests based on the user agent
+   characteristics, and "Transparent Content Negotiation"
+   (<xref target="RFC2295"/>), where content selection is performed by
+   an intermediary. These patterns are not mutually exclusive, and each has
+   trade-offs in applicability and practicality.
+</t>
+<t>
+   Note that, in all cases, HTTP is not aware of the resource semantics.
+   The consistency with which an origin server responds to requests, over time
+   and over the varying dimensions of content negotiation, and thus the
+   "sameness" of a resource's observed representations over time, is
+   determined entirely by whatever entity or algorithm selects or generates
+   those responses.
+</t>
+
+<section title="Proactive Negotiation" anchor="proactive.negotiation">
+  <x:anchor-alias value="proactive negotiation"/>
+  <x:anchor-alias value="server-driven negotiation"/>
+<t>
+   When content negotiation preferences are sent by the user agent in a
+   request to encourage an algorithm located at the server to
+   select the preferred representation, it is called
+   <x:dfn>proactive negotiation</x:dfn>
+   (a.k.a., <x:dfn>server-driven negotiation</x:dfn>). Selection is based on
+   the available representations for a response (the dimensions over which it
+   might vary, such as language, content coding, etc.) compared to various
+   information supplied in the request, including both the explicit
+   negotiation header fields below and implicit
+   characteristics, such as the client's network address or parts of the
+   <x:ref>User-Agent</x:ref> field.
+</t>
+<t>
+   Proactive negotiation is advantageous when the algorithm for
+   selecting from among the available representations is difficult to
+   describe to a user agent, or when the server desires to send its
+   "best guess" to the user agent along with the first response (when that
+   "best guess" is good enough for the user, this avoids the round-trip
+   delay of a subsequent request). In order to improve the server's
+   guess, a user agent <bcp14>MAY</bcp14> send request header fields that describe
+   its preferences.
+</t>
+<t>
+   Proactive negotiation has serious disadvantages:
+</t>
+<ul>
+   <li>
+    It is impossible for the server to accurately determine what
+    might be "best" for any given user, since that would require
+    complete knowledge of both the capabilities of the user agent
+    and the intended use for the response (e.g., does the user want
+    to view it on screen or print it on paper?);
+   </li>
+   <li>
+    Having the user agent describe its capabilities in every
+    request can be both very inefficient (given that only a small
+    percentage of responses have multiple representations) and a
+    potential risk to the user's privacy;
+   </li>
+   <li>
+    It complicates the implementation of an origin server and the
+    algorithms for generating responses to a request; and,
+   </li>
+   <li>
+    It limits the reusability of responses for shared caching.
+   </li>
+</ul>
+<t>
+   A user agent cannot rely on proactive negotiation preferences being
+   consistently honored, since the origin server might not implement proactive
+   negotiation for the requested resource or might decide that sending a
+   response that doesn't conform to the user agent's preferences is better
+   than sending a <x:ref>406 (Not Acceptable)</x:ref> response.
+</t>
+<t>
+   A <x:ref>Vary</x:ref> header field (<xref target="field.vary"/>) is
+   often sent in a response subject to proactive negotiation to indicate what
+   parts of the request information were used in the selection algorithm.
+</t>
+<t>
+   The request header fields <x:ref>Accept</x:ref>,
+   <x:ref>Accept-Charset</x:ref>, <x:ref>Accept-Encoding</x:ref>, and
+   <x:ref>Accept-Language</x:ref> are defined below for a user agent to engage
+   in <x:ref>proactive negotiation</x:ref> of the response content.
+   The preferences sent in these
+   fields apply to any content in the response, including representations of
+   the target resource, representations of error or processing status, and
+   potentially even the miscellaneous text strings that might appear within
+   the protocol.
+</t>
+</section>
+
+<section title="Reactive Negotiation" anchor="reactive.negotiation">
+  <x:anchor-alias value="reactive negotiation"/>
+  <x:anchor-alias value="agent-driven negotiation"/>
+<t>
+   With <x:dfn>reactive negotiation</x:dfn>
+   (a.k.a., <x:dfn>agent-driven negotiation</x:dfn>), selection of
+   content (regardless of the status code) is performed by
+   the user agent after receiving an initial response. The mechanism for
+   reactive negotiation might be as simple as a list of references to
+   alternative representations.
+</t>
+<t>
+   If the user agent is not satisfied by the initial response content,
+   it can perform a GET request on one or more of the alternative resources
+   to obtain a different representation. Selection of such alternatives might
+   be performed automatically (by the user agent) or manually (e.g., by the
+   user selecting from a hypertext menu).
+</t>
+<t>
+   A server might choose not to send an initial representation, other than
+   the list of alternatives, and thereby indicate that reactive
+   negotiation by the user agent is preferred. For example, the alternatives
+   listed in responses with the <x:ref>300 (Multiple Choices)</x:ref> and
+   <x:ref>406 (Not Acceptable)</x:ref> status codes include information about
+   available representations so that the user or user agent can react by
+   making a selection.
+</t>
+<t>
+   Reactive negotiation is advantageous when the response would vary
+   over commonly used dimensions (such as type, language, or encoding),
+   when the origin server is unable to determine a user agent's
+   capabilities from examining the request, and generally when public
+   caches are used to distribute server load and reduce network usage.
+</t>
+<t>
+   Reactive negotiation suffers from the disadvantages of transmitting
+   a list of alternatives to the user agent, which degrades user-perceived
+   latency if transmitted in the header section, and needing a second request
+   to obtain an alternate representation. Furthermore, this specification
+   does not define a mechanism for supporting automatic selection, though it
+   does not prevent such a mechanism from being developed.
+</t>
+</section>
+
+<section title="Request Content Negotiation" anchor="request.content.negotiation">
+  <x:anchor-alias value="request content negotiation"/>
+<t>
+   When content negotiation preferences are sent in a server's response, the
+   listed preferences are called <x:dfn>request content negotiation</x:dfn>
+   because they intend to influence selection of an appropriate content for
+   subsequent requests to that resource. For example,
+   the <x:ref>Accept</x:ref> (<xref target="field.accept"/>) and
+   <x:ref>Accept-Encoding</x:ref> (<xref target="field.accept-encoding"/>)
+   header fields can be sent in a response to indicate preferred media types
+   and content codings for subsequent requests to that resource.
+</t>
+<t>
+   Similarly, <xref target="RFC5789" x:sec="3.1" x:fmt="of"/> defines
+   the "Accept-Patch" response header field, which allows discovery of
+   which content types are accepted in PATCH requests.
+</t>
+</section>
+
+<section title="Content Negotiation Field Features" anchor="conneg.features">
+<section title="Absence" anchor="conneg.absent">
+<t>
+   For each of the content negotiation fields, a request that does not contain
+   the field implies that the sender has no preference on that dimension of
+   negotiation.
+</t>
+<t>
+   If a content negotiation header field is present in a request and none of
+   the available
+   representations for the response can be considered acceptable according to
+   it, the origin server can either honor the header field by sending a
+   <x:ref>406 (Not Acceptable)</x:ref> response or disregard the header field
+   by treating the response as if it is not subject to content negotiation
+   for that request header field. This does not imply, however, that the
+   client will be able to use the representation.
+</t>
+<aside><t>
+   <x:h>Note:</x:h> A user agent sending these header fields makes it easier for a
+   server to identify an individual by virtue of the user agent's request
+   characteristics (<xref target="fingerprinting"/>).
+</t></aside>
+</section>
+
+<section title="Quality Values" anchor="quality.values">
+  <x:anchor-alias value="weight"/>
+  <x:anchor-alias value="qvalue"/>
+<t>
+   The content negotiation fields defined by this specification
+   use a common parameter, named "q" (case-insensitive), to assign a relative
+   "weight" to the preference for that associated kind of content.
+   This weight is referred to as a "quality value" (or "qvalue") because
+   the same parameter name is often used within server configurations to
+   assign a weight to the relative quality of the various representations
+   that can be selected for a resource.
+</t>
+<t>
+   The weight is normalized to a real number in the range 0 through 1,
+   where 0.001 is the least preferred and 1 is the most preferred;
+   a value of 0 means "not acceptable". If no "q" parameter is present,
+   the default weight is 1.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="weight"/><iref primary="true" item="Grammar" subitem="qvalue"/>
+  <x:ref>weight</x:ref> = <x:ref>OWS</x:ref> ";" <x:ref>OWS</x:ref> "q=" <x:ref>qvalue</x:ref>
+  <x:ref>qvalue</x:ref> = ( "0" [ "." 0*3<x:ref>DIGIT</x:ref> ] )
+         / ( "1" [ "." 0*3("0") ] )
+</sourcecode>
+<t>
+   A sender of qvalue <bcp14>MUST NOT</bcp14> generate more than three digits after the
+   decimal point. User configuration of these values ought to be limited in
+   the same fashion.
+</t>
+</section>
+
+<section title="Wildcard Values" anchor="wildcard.values">
+  <x:anchor-alias value="wildcard"/>
+<t>
+   Most of these header fields, where indicated, define a wildcard value ("*")
+   to select unspecified values. If no wildcard is present, values that are
+   not explicitly mentioned in the field are considered unacceptable.
+   Within <x:ref>Vary</x:ref>, the wildcard value means that the variance
+   is unlimited.
+</t>
+<aside><t>
+   <x:h>Note:</x:h> In practice, using wildcards in content negotiation has limited
+   practical value because it is seldom useful to say, for example, "I
+   prefer image/* more or less than (some other specific value)". By sending
+   Accept: */*;q=0, clients can explicitly request a <x:ref>406 (Not Acceptable)</x:ref>
+   response if a more preferred format is not available, but
+   they still need to be able to handle a different response since the
+   server is allowed to ignore their preference.
+</t></aside>
+</section>
+</section>
+
+<section title="Content Negotiation Fields" anchor="conneg.fields">
+<section title="Accept" anchor="field.accept">
+  <x:anchor-alias value="header.accept"/>
+  <iref primary="true" item="Fields" subitem="Accept" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Accept" x:for-anchor=""/><iref primary="true" item="Accept header field" x:for-anchor=""/>
+  <x:anchor-alias value="Accept"/>
+  <x:anchor-alias value="accept-params"/>
+  <x:anchor-alias value="media-range"/>
+<t>
+   The "Accept" header field can be used by user agents to specify their
+   preferences regarding response media types. For example, Accept header
+   fields can be used to indicate that the request is specifically limited to
+   a small set of desired types, as in the case of a request for an in-line
+   image.
+</t>
+<t>
+   When sent by a server in a response, Accept provides information
+   about which content types are preferred in the content of a subsequent
+   request to the same resource.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Accept"/><iref primary="true" item="Grammar" subitem="media-range"/>
+  <x:ref>Accept</x:ref> = #( <x:ref>media-range</x:ref> [ <x:ref>weight</x:ref> ] )
+
+  <x:ref>media-range</x:ref>    = ( "*/*"
+                     / ( <x:ref>type</x:ref> "/" "*" )
+                     / ( <x:ref>type</x:ref> "/" <x:ref>subtype</x:ref> )
+                   ) <x:ref>parameters</x:ref>
+</sourcecode>
+<t>
+   The asterisk "*" character is used to group media types into ranges,
+   with "*/*" indicating all media types and "type/*" indicating all
+   subtypes of that type. The media-range can include media type
+   parameters that are applicable to that range.
+</t>
+<t>
+   Each media-range might be followed by optional applicable media type
+   parameters (e.g., <x:ref>charset</x:ref>), followed by an optional "q"
+   parameter for indicating a relative weight (<xref target="quality.values"/>).
+</t>
+<t>
+   Previous specifications allowed additional extension parameters to appear
+   after the weight parameter. The accept extension grammar (accept-params, accept-ext) has
+   been removed because it had a complicated definition, was not being used in
+   practice, and is more easily deployed through new header fields. Senders
+   using weights <bcp14>SHOULD</bcp14> send "q" last (after all media-range parameters).
+   Recipients <bcp14>SHOULD</bcp14> process any parameter named "q" as weight, regardless of
+   parameter ordering.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Use of the "q" parameter name to control content negotiation
+    would interfere with any media type parameter having the same name.
+    Hence, the media type registry disallows parameters named "q".
+  </t>
+</aside>
+<t>
+   The example
+</t>
+<sourcecode type="http-message">
+Accept: audio/*; q=0.2, audio/basic
+</sourcecode>
+<t>
+   is interpreted as "I prefer audio/basic, but send me any audio
+   type if it is the best available after an 80% markdown in quality".
+</t>
+<t>
+   A more elaborate example is
+</t>
+<sourcecode type="http-message">
+Accept: text/plain; q=0.5, text/html,
+       text/x-dvi; q=0.8, text/x-c
+</sourcecode>
+<t>
+   Verbally, this would be interpreted as "text/html and text/x-c are
+   the equally preferred media types, but if they do not exist, then send the
+   text/x-dvi representation, and if that does not exist, send the text/plain
+   representation".
+</t>
+<t>
+   Media ranges can be overridden by more specific media ranges or
+   specific media types. If more than one media range applies to a given
+   type, the most specific reference has precedence. For example,
+</t>
+<sourcecode type="http-message">
+Accept: text/*, text/plain, text/plain;format=flowed, */*
+</sourcecode>
+<t>
+   have the following precedence:
+</t>
+<ol>
+   <li>text/plain;format=flowed</li>
+   <li>text/plain</li>
+   <li>text/*</li>
+   <li>*/*</li>
+</ol>
+<t>
+   The media type quality factor associated with a given type is
+   determined by finding the media range with the highest precedence
+   that matches the type. For example,
+</t>
+<sourcecode type="http-message">
+Accept: text/*;q=0.3, text/plain;q=0.7, text/plain;format=flowed,
+       text/plain;format=fixed;q=0.4, */*;q=0.5
+</sourcecode>
+<t>
+   would cause the following values to be associated:
+</t>
+<table align="left">
+  <thead>
+    <tr>
+      <th>Media Type</th>
+      <th>Quality Value</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>text/plain;format=flowed</td>
+      <td>1</td>
+    </tr>
+    <tr>
+      <td>text/plain</td>
+      <td>0.7</td>
+    </tr>
+    <tr>
+      <td>text/html</td>
+      <td>0.3</td>
+    </tr>
+    <tr>
+      <td>image/jpeg</td>
+      <td>0.5</td>
+    </tr>
+    <tr>
+      <td>text/plain;format=fixed</td>
+      <td>0.4</td>
+    </tr>
+    <tr>
+      <td>text/html;level=3</td>
+      <td>0.7</td>
+    </tr>
+  </tbody>
+</table>
+<aside><t>
+   <x:h>Note:</x:h> A user agent might be provided with a default set of quality
+   values for certain media ranges. However, unless the user agent is
+   a closed system that cannot interact with other rendering agents,
+   this default set ought to be configurable by the user.
+</t></aside>
+</section>
+
+<section title="Accept-Charset" anchor="field.accept-charset">
+  <x:anchor-alias value="header.accept-charset"/>
+   <rdf:Description>
+     <status xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">deprecated</status>
+   </rdf:Description>
+  <iref primary="true" item="Fields" subitem="Accept-Charset" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Accept-Charset" x:for-anchor=""/><iref primary="true" item="Accept-Charset header field" x:for-anchor=""/>
+  <x:anchor-alias value="Accept-Charset"/>
+<t>
+   The "Accept-Charset" header field can be sent by a user agent to indicate
+   its preferences for charsets in textual response content. For example,
+   this field allows user agents capable of understanding more comprehensive
+   or special-purpose charsets to signal that capability to an origin server
+   that is capable of representing information in those charsets.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Accept-Charset"/>
+  <x:ref>Accept-Charset</x:ref> = #( ( <x:ref>token</x:ref> / "*" ) [ <x:ref>weight</x:ref> ] )
+</sourcecode>
+<t>
+   Charset names are defined in <xref target="charset"/>.
+   A user agent <bcp14>MAY</bcp14> associate a quality value with each charset to indicate
+   the user's relative preference for that charset, as defined in <xref target="quality.values"/>.
+   An example is
+</t>
+<sourcecode type="http-message">
+Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
+</sourcecode>
+<t>
+   The special value "*", if present in the Accept-Charset header field,
+   matches every charset that is not mentioned elsewhere in the
+   field.
+</t>
+<aside><t>
+   <x:h>Note:</x:h> Accept-Charset is deprecated because UTF-8 has become nearly
+   ubiquitous and sending a detailed list of user-preferred charsets wastes
+   bandwidth, increases latency, and makes passive fingerprinting far too
+   easy (<xref target="fingerprinting"/>). Most general-purpose user agents
+   do not send Accept-Charset unless specifically configured to do so.
+</t></aside>
+</section>
+
+<section title="Accept-Encoding" anchor="field.accept-encoding">
+  <x:anchor-alias value="header.accept-encoding"/>
+  <iref primary="true" item="Fields" subitem="Accept-Encoding" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Accept-Encoding" x:for-anchor=""/><iref primary="true" item="Accept-Encoding header field" x:for-anchor=""/>
+  <x:anchor-alias value="Accept-Encoding"/>
+  <x:anchor-alias value="codings"/>
+<t>
+   The "Accept-Encoding" header field can be used to indicate preferences
+   regarding the use of content codings (<xref target="content.codings"/>).
+</t>
+<t>
+   When sent by a user agent in a request, Accept-Encoding indicates the
+   content codings acceptable in a response.
+</t>
+<t>
+   When sent by a server in a response, Accept-Encoding provides information
+   about which content codings are preferred in the content of a subsequent
+   request to the same resource.
+</t>
+<t>
+   An "identity" token is used as a synonym for
+   "no encoding" in order to communicate when no encoding is preferred.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Accept-Encoding"/><iref primary="true" item="Grammar" subitem="codings"/>
+  <x:ref>Accept-Encoding</x:ref>  = #( <x:ref>codings</x:ref> [ <x:ref>weight</x:ref> ] )
+  <x:ref>codings</x:ref>          = <x:ref>content-coding</x:ref> / "identity" / "*"
+</sourcecode>
+<t>
+   Each codings value <bcp14>MAY</bcp14> be given an associated quality value (weight)
+   representing the preference for that encoding, as defined in <xref target="quality.values"/>.
+   The asterisk "*" symbol in an Accept-Encoding field matches any available
+   content coding not explicitly listed in the field.
+</t>
+<t>
+   Examples:
+</t>
+<sourcecode type="http-message">
+Accept-Encoding: compress, gzip
+Accept-Encoding:
+Accept-Encoding: *
+Accept-Encoding: compress;q=0.5, gzip;q=1.0
+Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
+</sourcecode>
+<t>
+   A server tests whether a content coding for a given representation is
+   acceptable using these rules:
+</t>
+<ol>
+   <li>If no Accept-Encoding header field is in the request, any content coding is
+       considered acceptable by the user agent.</li>
+   <li>If the representation has no content coding, then it is acceptable
+       by default unless specifically excluded by the Accept-Encoding header field
+       stating either "identity;q=0" or "*;q=0" without a more specific
+       entry for "identity".</li>
+   <li>If the representation's content coding is one of the content codings
+       listed in the Accept-Encoding field value, then it is acceptable unless
+       it is accompanied by a qvalue of 0. (As defined in <xref target="quality.values"/>, a
+       qvalue of 0 means "not acceptable".)</li>
+</ol>
+<t>
+   A representation could be encoded with multiple content codings. However, most
+   content codings are alternative ways to accomplish the same purpose
+   (e.g., data compression). When selecting between multiple content codings that
+   have the same purpose, the acceptable content coding with the highest
+   non-zero qvalue is preferred.
+</t>
+<t>
+   An Accept-Encoding header field with a field value that is empty
+   implies that the user agent does not want any content coding in response.
+   If a non-empty Accept-Encoding header field is present in a request and none of the
+   available representations for the response have a content coding that
+   is listed as acceptable, the origin server <bcp14>SHOULD</bcp14> send a response
+   without any content coding unless the identity coding is indicated as unacceptable.
+</t>
+<t>
+   When the Accept-Encoding header field is present in a response, it indicates
+   what content codings the resource was willing to accept in the associated
+   request. The field value is evaluated the same way as in a request.
+</t>
+<t>
+   Note that this information is specific to the associated request; the set of
+   supported encodings might be different for other resources on the same
+   server and could change over time or depend on other aspects of the request
+   (such as the request method).
+</t>
+<t>
+   Servers that fail a request due to an unsupported content coding ought to
+   respond with a <x:ref>415 (Unsupported Media Type)</x:ref> status and
+   include an Accept-Encoding header field in that response, allowing
+   clients to distinguish between issues related to content codings and media
+   types. In order to avoid confusion with issues related to media types,
+   servers that fail a request with a 415 status for reasons unrelated to
+   content codings <bcp14>MUST NOT</bcp14> include the Accept-Encoding header
+   field.
+</t>
+<t>
+   The most common use of Accept-Encoding is in responses with a
+   <x:ref>415 (Unsupported Media Type)</x:ref> status code, in response to
+   optimistic use of a content coding by clients. However, the header field
+   can also be used to indicate to clients that content codings are supported
+   in order to optimize future interactions. For example, a resource might include it
+   in a <x:ref>2xx (Successful)</x:ref> response when the request content was
+   big enough to justify use of a compression coding but the client failed do
+   so.
+</t>
+</section>
+
+<section title="Accept-Language" anchor="field.accept-language">
+  <x:anchor-alias value="header.accept-language"/>
+  <iref primary="true" item="Fields" subitem="Accept-Language" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Accept-Language" x:for-anchor=""/><iref primary="true" item="Accept-Language header field" x:for-anchor=""/>
+  <x:anchor-alias value="Accept-Language"/>
+  <x:anchor-alias value="language-range"/>
+<t>
+   The "Accept-Language" header field can be used by user agents to
+   indicate the set of natural languages that are preferred in the response.
+   Language tags are defined in <xref target="language.tags"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Accept-Language"/><iref primary="true" item="Grammar" subitem="language-range"/>
+  <x:ref>Accept-Language</x:ref> = #( <x:ref>language-range</x:ref> [ <x:ref>weight</x:ref> ] )
+  <x:ref>language-range</x:ref>  =
+            &lt;language-range, see <xref target="RFC4647" x:fmt="," x:sec="2.1"/>&gt;
+</sourcecode>
+<t>
+   Each language-range can be given an associated quality value
+   representing an estimate of the user's preference for the languages
+   specified by that range, as defined in <xref target="quality.values"/>. For example,
+</t>
+<sourcecode type="http-message">
+Accept-Language: da, en-gb;q=0.8, en;q=0.7
+</sourcecode>
+<t>
+   would mean: "I prefer Danish, but will accept British English and
+   other types of English".
+</t>
+<t>
+   Note that some recipients treat the order in which language tags are listed
+   as an indication of descending priority, particularly for tags that are
+   assigned equal quality values (no value is the same as q=1). However, this
+   behavior cannot be relied upon. For consistency and to maximize
+   interoperability, many user agents assign each language tag a unique
+   quality value while also listing them in order of decreasing quality.
+   Additional discussion of language priority lists can be found in
+   <xref target="RFC4647" x:sec="2.3" x:fmt="of"/>.
+</t>
+<t>
+   For matching, <xref target="RFC4647" x:sec="3" x:fmt="of"/> defines
+   several matching schemes. Implementations can offer the most appropriate
+   matching scheme for their requirements. The "Basic Filtering" scheme
+   (<xref target="RFC4647" x:fmt="," x:sec="3.3.1"/>) is identical to the
+   matching scheme that was previously defined for HTTP in
+   <xref target="RFC2616" x:fmt="of" x:sec="14.4"/>.
+</t>
+<t>
+   It might be contrary to the privacy expectations of the user to send
+   an Accept-Language header field with the complete linguistic preferences of
+   the user in every request (<xref target="fingerprinting"/>).
+</t>
+<t>
+   Since intelligibility is highly dependent on the individual user, user
+   agents need to allow user control over the linguistic preference (either
+   through configuration of the user agent itself or by defaulting to a user
+   controllable system setting).
+   A user agent that does not provide such control to the user <bcp14>MUST NOT</bcp14>
+   send an Accept-Language header field.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> User agents ought to provide guidance to users when setting a
+    preference, since users are rarely familiar with the details of language
+    matching as described above. For example, users might assume that on
+    selecting "en-gb", they will be served any kind of English document if
+    British English is not available. A user agent might suggest, in such a
+    case, to add "en" to the list for better matching behavior.
+  </t>
+</aside>
+</section>
+
+<section title="Vary" anchor="field.vary">
+  <x:anchor-alias value="header.vary"/>
+   <iref primary="true" item="Fields" subitem="Vary" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Vary" x:for-anchor=""/><iref item="Vary header field" primary="true" x:for-anchor=""/>
+   <x:anchor-alias value="Vary"/>
+<t>
+   The "Vary" header field in a response describes what parts of a request
+   message, aside from the method and target URI, might have influenced the
+   origin server's process for selecting the content of this response.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Vary"/>
+  <x:ref>Vary</x:ref> = #( "*" / <x:ref>field-name</x:ref> )
+</sourcecode>
+<t>
+   A Vary field value is either the wildcard member "*" or a list of
+   request field names, known as the selecting header fields, that might
+   have had a role in selecting the representation for this response.
+   Potential selecting header fields are not limited to fields defined by
+   this specification.
+</t>
+<t>
+   A list containing the member "*" signals that other aspects of the
+   request might have played a role in selecting the response representation,
+   possibly including aspects outside the message syntax (e.g., the
+   client's network address).
+   A recipient will not be able to determine whether this response is
+   appropriate for a later request without forwarding the request to the
+   origin server. A proxy <bcp14>MUST NOT</bcp14> generate "*" in a Vary field value.
+</t>
+<t>
+   For example, a response that contains
+</t>
+<sourcecode type="http-message">
+Vary: accept-encoding, accept-language
+</sourcecode>
+<t>
+   indicates that the origin server might have used the request's
+   <x:ref>Accept-Encoding</x:ref> and <x:ref>Accept-Language</x:ref>
+   header fields (or lack thereof) as determining factors while choosing
+   the content for this response.
+</t>
+<t>
+   A Vary field containing a list of field names has two purposes:
+</t>
+<ol>
+  <li>
+    <t>
+       To inform cache recipients that they <bcp14>MUST NOT</bcp14> use this response
+       to satisfy a later request unless the later request has the
+       same values for the listed header fields as the original request
+       (<xref target="CACHING" x:rel="#caching.negotiated.responses"/>) or reuse of the
+       response has been validated by the origin server.
+       In other words, Vary expands the cache key
+       required to match a new request to the stored cache entry.
+    </t>
+  </li>
+  <li>
+    <t>
+       To inform user agent recipients that this response was subject to
+       content negotiation (<xref target="content.negotiation"/>) and a
+       different representation might be sent in a subsequent request if
+       other values are provided in the listed header fields
+       (<x:ref>proactive negotiation</x:ref>).
+    </t>
+  </li>
+</ol>
+<t>
+   An origin server <bcp14>SHOULD</bcp14> generate a Vary header field on a cacheable
+   response when it wishes that response to be selectively reused for
+   subsequent requests. Generally, that is the case when the response
+   content has been tailored to better fit the preferences expressed by
+   those selecting header fields, such as when an origin server has
+   selected the response's language based on the request's
+   <x:ref>Accept-Language</x:ref> header field.
+</t>
+<t>
+   Vary might be elided when an origin server considers variance in
+   content selection to be less significant than Vary's performance impact
+   on caching, particularly when reuse is already limited by cache
+   response directives (<xref target="CACHING" x:rel="#field.cache-control"/>).
+</t>
+<t>
+   There is no need to send the Authorization field name in Vary because
+   reuse of that response for a different user is prohibited by the field
+   definition (<xref target="field.authorization"/>).
+   Likewise, if the response content has been selected or influenced by
+   network region, but the origin server wants the cached response to be
+   reused even if recipients move from one region to another, then there
+   is no need for the origin server to indicate such variance in Vary.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Conditional Requests" anchor="conditional.requests">
+  <iref item="conditional request" primary="true"/>
+<t>
+   A conditional request is an HTTP request with one or more request header
+   fields that indicate a precondition to be tested before
+   applying the request method to the target resource.
+   <xref target="evaluation"/> defines when to evaluate preconditions and
+   their order of precedence when more than one precondition is present.
+</t>
+<t>
+   Conditional GET requests are the most efficient mechanism for HTTP
+   cache updates <xref target="CACHING"/>.  Conditionals can also be
+   applied to state-changing methods, such as PUT and DELETE, to prevent
+   the "lost update" problem: one client accidentally overwriting
+   the work of another client that has been acting in parallel.
+</t>
+
+<section title="Preconditions" anchor="preconditions">
+   <iref primary="false" item="selected representation"/>
+<t>
+   Preconditions are usually defined with respect to a state of the target
+   resource as a whole (its current value set) or the state as observed in a
+   previously obtained representation (one value in that set). If a resource
+   has multiple current representations, each with its own observable state,
+   a precondition will assume that the mapping of each request to a
+   <x:ref>selected representation</x:ref> (<xref target="representations"/>)
+   is consistent over time.
+   Regardless, if the mapping is inconsistent or the server is unable to
+   select an appropriate representation, then no harm will result when the
+   precondition evaluates to false.
+</t>
+<t>
+   Each precondition defined below consists of a comparison between a
+   set of validators obtained from prior representations of the target
+   resource to the current state of validators for the selected
+   representation (<xref target="response.validator"/>). Hence, these
+   preconditions evaluate whether the state of the target resource has
+   changed since a given state known by the client. The effect of such an
+   evaluation depends on the method semantics and choice of conditional, as
+   defined in <xref target="evaluation"/>.
+</t>
+<t>
+   Other preconditions, defined by other specifications as extension fields,
+   might place conditions on all recipients, on the state of the target
+   resource in general, or on a group of resources. For instance, the "If"
+   header field in WebDAV can make a request conditional on various aspects
+   of multiple resources, such as locks, if the recipient understands and
+   implements that field (<xref target="WEBDAV" x:fmt="," x:sec="10.4"/>).
+</t>
+<t>
+   Extensibility of preconditions is only possible when the precondition can
+   be safely ignored if unknown (like <x:ref>If-Modified-Since</x:ref>), when
+   deployment can be assumed for a given use case, or when implementation
+   is signaled by some other property of the target resource. This encourages
+   a focus on mutually agreed deployment of common standards.
+</t>
+
+<section title="If-Match" anchor="field.if-match">
+  <x:anchor-alias value="header.if-match"/>
+  <iref primary="true" item="Fields" subitem="If-Match" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="If-Match" x:for-anchor=""/><iref primary="true" item="If-Match header field" x:for-anchor=""/>
+  <x:anchor-alias value="If-Match"/>
+<t>
+   The "If-Match" header field makes the request method conditional on the
+   recipient origin server either having at least one current
+   representation of the target resource, when the field value is "*", or
+   having a current representation of the target resource that has an
+   entity tag matching a member of the list of entity tags provided in the
+   field value.
+</t>
+<t>
+   An origin server <bcp14>MUST</bcp14> use the strong comparison function when comparing
+   entity tags for If-Match (<xref target="entity.tag.comparison"/>), since
+   the client intends this precondition to prevent the method from being
+   applied if there have been any changes to the representation data.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="If-Match"/>
+  <x:ref>If-Match</x:ref> = "*" / #<x:ref>entity-tag</x:ref>
+</sourcecode>
+<t>
+   Examples:
+</t>
+<sourcecode type="http-message">
+If-Match: "xyzzy"
+If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+If-Match: *
+</sourcecode>
+<t>
+   If-Match is most often used with state-changing methods (e.g., POST, PUT,
+   DELETE) to prevent accidental overwrites when multiple user agents might be
+   acting in parallel on the same resource (i.e., to prevent the "lost update"
+   problem). In general, it can be used with any method that involves the
+   selection or modification of a representation to abort the request if the
+   <x:ref>selected representation</x:ref>'s current entity tag is not a
+   member within the If-Match field value.
+</t>
+<t>
+   When an origin server receives a request that selects a representation
+   and that request includes an If-Match header field,
+   the origin server <bcp14>MUST</bcp14> evaluate the If-Match condition per
+   <xref target="evaluation"/> prior to performing the method.
+</t>
+<t>
+   To evaluate a received If-Match header field:
+</t>
+<ol>
+   <li>
+      If the field value is "*", the condition is true if the origin server
+      has a current representation for the target resource.
+   </li>
+   <li>
+      If the field value is a list of entity tags, the condition is true if
+      any of the listed tags match the entity tag of the selected representation.
+   </li>
+   <li>
+      Otherwise, the condition is false.
+   </li>
+</ol>
+<t>
+   An origin server that evaluates an If-Match condition <bcp14>MUST NOT</bcp14> perform
+   the requested method if the condition evaluates to false. Instead,
+   the origin server <bcp14>MAY</bcp14>
+   indicate that the conditional request failed by responding with a
+   <x:ref>412 (Precondition Failed)</x:ref> status code. Alternatively,
+   if the request is a state-changing operation that appears to have already
+   been applied to the selected representation, the origin server <bcp14>MAY</bcp14> respond
+   with a <x:ref>2xx (Successful)</x:ref> status code
+   (i.e., the change requested by the user agent has already succeeded, but
+   the user agent might not be aware of it, perhaps because the prior response
+   was lost or an equivalent change was made by some other user agent).
+</t>
+<t>
+   Allowing an origin server to send a success response when a change request
+   appears to have already been applied is more efficient for many authoring
+   use cases, but comes with some risk if multiple user agents are making
+   change requests that are very similar but not cooperative.
+   For example, multiple user agents writing to a common resource as a
+   semaphore (e.g., a nonatomic increment) are likely to collide and
+   potentially lose important state transitions. For those kinds of resources,
+   an origin server is better off being stringent in sending 412 for every
+   failed precondition on an unsafe method.
+   In other cases, excluding the ETag field from a success response might
+   encourage the user agent to perform a GET as its next request to eliminate
+   confusion about the resource's current state.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> send an If-Match header field in a
+   <x:ref>GET</x:ref> request to indicate that it would prefer a
+   <x:ref>412 (Precondition Failed)</x:ref> response if the selected
+   representation does not match. However, this is only useful in range
+   requests (<xref target="range.requests"/>) for completing a previously
+   received partial representation when there is no desire for a new
+   representation. <x:ref>If-Range</x:ref> (<xref target="field.if-range"/>)
+   is better suited for range requests when the client prefers to receive a
+   new representation.
+</t>
+<t>
+   A cache or intermediary <bcp14>MAY</bcp14> ignore If-Match because its
+   interoperability features are only necessary for an origin server.
+</t>
+<t>
+   Note that an If-Match header field with a list value containing "*" and
+   other values (including other instances of "*") is syntactically
+   invalid (therefore not allowed to be generated) and furthermore is
+   unlikely to be interoperable.
+</t>
+</section>
+
+<section title="If-None-Match" anchor="field.if-none-match">
+  <x:anchor-alias value="header.if-none-match"/>
+  <iref primary="true" item="Fields" subitem="If-None-Match" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="If-None-Match" x:for-anchor=""/><iref primary="true" item="If-None-Match header field" x:for-anchor=""/>
+  <x:anchor-alias value="If-None-Match"/>
+<t>
+   The "If-None-Match" header field makes the request method conditional on
+   a recipient cache or origin server either not having any current
+   representation of the target resource, when the field value is "*", or
+   having a <x:ref>selected representation</x:ref> with an entity tag that does not match any
+   of those listed in the field value.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> use the weak comparison function when comparing
+   entity tags for If-None-Match (<xref target="entity.tag.comparison"/>),
+   since weak entity tags can be used for cache validation even if there have
+   been changes to the representation data.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="If-None-Match"/>
+  <x:ref>If-None-Match</x:ref> = "*" / #<x:ref>entity-tag</x:ref>
+</sourcecode>
+<t>
+   Examples:
+</t>
+<sourcecode type="http-message">
+If-None-Match: "xyzzy"
+If-None-Match: W/"xyzzy"
+If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
+If-None-Match: *
+</sourcecode>
+<t>
+   If-None-Match is primarily used in conditional GET requests to enable
+   efficient updates of cached information with a minimum amount of
+   transaction overhead. When a client desires to update one or more stored
+   responses that have entity tags, the client <bcp14>SHOULD</bcp14> generate an
+   If-None-Match header field containing a list of those entity tags when
+   making a GET request; this allows recipient servers to send a
+   <x:ref>304 (Not Modified)</x:ref> response to indicate when one of those
+   stored responses matches the selected representation.
+</t>
+<t>
+   If-None-Match can also be used with a value of "*" to prevent an unsafe
+   request method (e.g., PUT) from inadvertently modifying an existing
+   representation of the target resource when the client believes that
+   the resource does not have a current representation (<xref target="safe.methods"/>).
+   This is a variation on the "lost update" problem that might arise if more
+   than one client attempts to create an initial representation for the target
+   resource.
+</t>
+<t>
+   When an origin server receives a request that selects a representation
+   and that request includes an If-None-Match header field,
+   the origin server <bcp14>MUST</bcp14> evaluate the If-None-Match condition per
+   <xref target="evaluation"/> prior to performing the method.
+</t>
+<t>
+   To evaluate a received If-None-Match header field:
+</t>
+<ol>
+   <li>
+      If the field value is "*", the condition is false if the origin server
+      has a current representation for the target resource.
+   </li>
+   <li>
+      If the field value is a list of entity tags, the condition is false if
+      one of the listed tags matches the entity tag of the selected representation.
+   </li>
+   <li>
+      Otherwise, the condition is true.
+   </li>
+</ol>
+<t>
+   An origin server that evaluates an If-None-Match condition <bcp14>MUST NOT</bcp14>
+   perform the requested method if the condition evaluates to false; instead,
+   the origin server <bcp14>MUST</bcp14> respond with either
+   a) the <x:ref>304 (Not Modified)</x:ref> status code if the request method
+   is GET or HEAD or b) the <x:ref>412 (Precondition Failed)</x:ref> status
+   code for all other request methods.
+</t>
+<t>
+   Requirements on cache handling of a received If-None-Match header field
+   are defined in <xref target="CACHING" x:rel="#validation.received"/>.
+</t>
+<t>
+   Note that an If-None-Match header field with a list value containing "*" and
+   other values (including other instances of "*") is syntactically
+   invalid (therefore not allowed to be generated) and furthermore is
+   unlikely to be interoperable.
+</t>
+</section>
+
+<section title="If-Modified-Since" anchor="field.if-modified-since">
+  <x:anchor-alias value="header.if-modified-since"/>
+  <iref primary="true" item="Fields" subitem="If-Modified-Since" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="If-Modified-Since" x:for-anchor=""/><iref primary="true" item="If-Modified-Since header field" x:for-anchor=""/>
+  <x:anchor-alias value="If-Modified-Since"/>
+<t>
+   The "If-Modified-Since" header field makes a GET or HEAD request method
+   conditional on the <x:ref>selected representation</x:ref>'s modification
+   date being more
+   recent than the date provided in the field value. Transfer of the selected
+   representation's data is avoided if that data has not changed.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="If-Modified-Since"/>
+  <x:ref>If-Modified-Since</x:ref> = <x:ref>HTTP-date</x:ref>
+</sourcecode>
+<t>
+   An example of the field is:
+</t>
+<sourcecode type="http-message">
+If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+</sourcecode>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore If-Modified-Since if the request contains an
+   <x:ref>If-None-Match</x:ref> header field; the condition in
+   <x:ref>If-None-Match</x:ref> is considered to be a more accurate
+   replacement for the condition in If-Modified-Since, and the two are only
+   combined for the sake of interoperating with older intermediaries that
+   might not implement <x:ref>If-None-Match</x:ref>.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore the If-Modified-Since header field if the
+   received field value is not a valid HTTP-date, the field value has more than
+   one member, or if the request method is neither GET nor HEAD.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore the If-Modified-Since header field if the
+   resource does not have a modification date available.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> interpret an If-Modified-Since field value's timestamp
+   in terms of the origin server's clock.
+</t>
+<t>
+   If-Modified-Since is typically used for two distinct purposes:
+   1) to allow efficient updates of a cached representation that does not
+   have an entity tag and 2) to limit the scope of a web traversal to resources
+   that have recently changed.
+</t>
+<t>
+   When used for cache updates, a cache will typically use the value of the
+   cached message's <x:ref>Last-Modified</x:ref> header field to generate the field
+   value of If-Modified-Since. This behavior is most interoperable for cases
+   where clocks are poorly synchronized or when the server has chosen to only
+   honor exact timestamp matches (due to a problem with Last-Modified dates
+   that appear to go "back in time" when the origin server's clock is
+   corrected or a representation is restored from an archived backup).
+   However, caches occasionally generate the field value based on other data,
+   such as the <x:ref>Date</x:ref> header field of the cached message or the
+   clock time at which the message was received, particularly when the
+   cached message does not contain a <x:ref>Last-Modified</x:ref> header field.
+</t>
+<t>
+   When used for limiting the scope of retrieval to a recent time window, a
+   user agent will generate an If-Modified-Since field value based on either
+   its own clock or a <x:ref>Date</x:ref> header field received from the
+   server in a prior response. Origin servers that choose an exact
+   timestamp match based on the selected representation's
+   <x:ref>Last-Modified</x:ref>
+   header field will not be able to help the user agent limit its data
+   transfers to only those changed during the specified window.
+</t>
+<t>
+   When an origin server receives a request that selects a representation
+   and that request includes an If-Modified-Since header field without an
+   <x:ref>If-None-Match</x:ref> header field, the origin server <bcp14>SHOULD</bcp14>
+   evaluate the If-Modified-Since condition per
+   <xref target="evaluation"/> prior to performing the method.
+</t>
+<t>
+   To evaluate a received If-Modified-Since header field:
+</t>
+<ol>
+   <li>
+      If the selected representation's last modification date is earlier or
+      equal to the date provided in the field value, the condition is false.
+   </li>
+   <li>
+      Otherwise, the condition is true.
+   </li>
+</ol>
+<t>
+   An origin server that evaluates an If-Modified-Since condition
+   <bcp14>SHOULD NOT</bcp14> perform the requested method if the condition evaluates to
+   false; instead,
+   the origin server <bcp14>SHOULD</bcp14> generate a <x:ref>304 (Not Modified)</x:ref>
+   response, including only those metadata that are useful for identifying or
+   updating a previously cached response.
+</t>
+<t>
+   Requirements on cache handling of a received If-Modified-Since header field
+   are defined in <xref target="CACHING" x:rel="#validation.received"/>.
+</t>
+</section>
+
+<section title="If-Unmodified-Since" anchor="field.if-unmodified-since">
+  <x:anchor-alias value="header.if-unmodified-since"/>
+  <iref primary="true" item="Fields" subitem="If-Unmodified-Since" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="If-Unmodified-Since" x:for-anchor=""/><iref primary="true" item="If-Unmodified-Since header field" x:for-anchor=""/>
+  <x:anchor-alias value="If-Unmodified-Since"/>
+<t>
+   The "If-Unmodified-Since" header field makes the request method conditional
+   on the <x:ref>selected representation</x:ref>'s last modification date being
+   earlier than or equal to the date provided in the field value.
+   This field accomplishes the
+   same purpose as <x:ref>If-Match</x:ref> for cases where the user agent does
+   not have an entity tag for the representation.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="If-Unmodified-Since"/>
+  <x:ref>If-Unmodified-Since</x:ref> = <x:ref>HTTP-date</x:ref>
+</sourcecode>
+<t>
+   An example of the field is:
+</t>
+<sourcecode type="http-message">
+If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+</sourcecode>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore If-Unmodified-Since if the request contains an
+   <x:ref>If-Match</x:ref> header field; the condition in
+   <x:ref>If-Match</x:ref> is considered to be a more accurate replacement for
+   the condition in If-Unmodified-Since, and the two are only combined for the
+   sake of interoperating with older intermediaries that might not implement
+   <x:ref>If-Match</x:ref>.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore the If-Unmodified-Since header field if the
+   received field value is not a valid HTTP-date (including when the field
+   value appears to be a list of dates).
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore the If-Unmodified-Since header field if the
+   resource does not have a modification date available.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> interpret an If-Unmodified-Since field value's timestamp
+   in terms of the origin server's clock.
+</t>
+<t>
+   If-Unmodified-Since is most often used with state-changing methods
+   (e.g., POST, PUT, DELETE) to prevent accidental overwrites when multiple
+   user agents might be acting in parallel on a resource that does
+   not supply entity tags with its representations (i.e., to prevent the
+   "lost update" problem).
+   In general, it can be used with any method that involves the selection
+   or modification of a representation to abort the request if the
+   <x:ref>selected representation</x:ref>'s last modification date has
+   changed since the date provided in the If-Unmodified-Since field value.
+</t>
+<t>
+   When an origin server receives a request that selects a representation
+   and that request includes an If-Unmodified-Since header field without
+   an <x:ref>If-Match</x:ref> header field,
+   the origin server <bcp14>MUST</bcp14> evaluate the If-Unmodified-Since condition per
+   <xref target="evaluation"/> prior to performing the method.
+</t>
+<t>
+   To evaluate a received If-Unmodified-Since header field:
+</t>
+<ol>
+   <li>
+      If the selected representation's last modification date is earlier than or
+      equal to the date provided in the field value, the condition is true.
+   </li>
+   <li>
+      Otherwise, the condition is false.
+   </li>
+</ol>
+<t>
+   An origin server that evaluates an If-Unmodified-Since condition <bcp14>MUST NOT</bcp14>
+   perform the requested method if the condition evaluates to false.
+   Instead, the origin server <bcp14>MAY</bcp14> indicate that the conditional request
+   failed by responding with a <x:ref>412 (Precondition Failed)</x:ref>
+   status code. Alternatively, if the request is a state-changing operation
+   that appears to have already been applied to the selected representation,
+   the origin server <bcp14>MAY</bcp14> respond with a <x:ref>2xx (Successful)</x:ref>
+   status code
+   (i.e., the change requested by the user agent has already succeeded, but
+   the user agent might not be aware of it, perhaps because the prior response
+   was lost or an equivalent change was made by some other user agent).
+</t>
+<t>
+   Allowing an origin server to send a success response when a change request
+   appears to have already been applied is more efficient for many authoring
+   use cases, but comes with some risk if multiple user agents are making
+   change requests that are very similar but not cooperative.
+   In those cases, an origin server is better off being stringent in sending
+   412 for every failed precondition on an unsafe method.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> send an If-Unmodified-Since header field in a
+   <x:ref>GET</x:ref> request to indicate that it would prefer a
+   <x:ref>412 (Precondition Failed)</x:ref> response if the selected
+   representation has been modified. However, this is only useful in range
+   requests (<xref target="range.requests"/>) for completing a previously
+   received partial representation when there is no desire for a new
+   representation. <x:ref>If-Range</x:ref> (<xref target="field.if-range"/>)
+   is better suited for range requests when the client prefers to receive a
+   new representation.
+</t>
+<t>
+   A cache or intermediary <bcp14>MAY</bcp14> ignore If-Unmodified-Since because its
+   interoperability features are only necessary for an origin server.
+</t>
+</section>
+
+<section title="If-Range" anchor="field.if-range">
+  <x:anchor-alias value="header.if-range"/>
+  <iref primary="true" item="Fields" subitem="If-Range" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="If-Range" x:for-anchor=""/><iref primary="true" item="If-Range header field" x:for-anchor=""/>
+  <x:anchor-alias value="If-Range"/>
+<t>
+   The "If-Range" header field provides a special conditional request
+   mechanism that is similar to the <x:ref>If-Match</x:ref> and
+   <x:ref>If-Unmodified-Since</x:ref> header fields but that instructs the
+   recipient to ignore the <x:ref>Range</x:ref> header field if the validator
+   doesn't match, resulting in transfer of the new <x:ref>selected representation</x:ref>
+   instead of a <x:ref>412 (Precondition Failed)</x:ref> response.
+</t>
+<t>
+   If a client has a partial copy of a representation and wishes
+   to have an up-to-date copy of the entire representation, it could use the
+   <x:ref>Range</x:ref> header field with a conditional GET (using
+   either or both of <x:ref>If-Unmodified-Since</x:ref> and
+   <x:ref>If-Match</x:ref>.) However, if the precondition fails because the
+   representation has been modified, the client would then have to make a
+   second request to obtain the entire current representation.
+</t>
+<t>
+   The "If-Range" header field allows a client to "short-circuit" the second
+   request. Informally, its meaning is as follows: if the representation is unchanged,
+   send me the part(s) that I am requesting in Range; otherwise, send me the
+   entire representation.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="If-Range"/>
+  <x:ref>If-Range</x:ref> = <x:ref>entity-tag</x:ref> / <x:ref>HTTP-date</x:ref>
+</sourcecode>
+<t>
+   A valid <x:ref>entity-tag</x:ref> can be distinguished from a valid
+   <x:ref>HTTP-date</x:ref> by examining the first three characters for a
+   DQUOTE.
+</t>
+<t>
+   A client <bcp14>MUST NOT</bcp14> generate an If-Range header field in a request that
+   does not contain a <x:ref>Range</x:ref> header field.
+   A server <bcp14>MUST</bcp14> ignore an If-Range header field received in a request that
+   does not contain a <x:ref>Range</x:ref> header field.
+   An origin server <bcp14>MUST</bcp14> ignore an If-Range header field received in a
+   request for a target resource that does not support Range requests.
+</t>
+<t>
+   A client <bcp14>MUST NOT</bcp14> generate an If-Range header field containing an
+   entity tag that is marked as weak.
+   A client <bcp14>MUST NOT</bcp14> generate an If-Range header field containing an
+   <x:ref>HTTP-date</x:ref> unless the client has no entity tag for
+   the corresponding representation and the date is a strong validator
+   in the sense defined by <xref target="lastmod.comparison"/>.
+</t>
+<t>
+   A server that receives an If-Range header field on a Range request <bcp14>MUST</bcp14>
+   evaluate the condition per <xref target="evaluation"/> prior to
+   performing the method.
+</t>
+<t>
+   To evaluate a received If-Range header field containing an
+   <x:ref>HTTP-date</x:ref>:
+</t>
+<ol>
+   <li>If the <x:ref>HTTP-date</x:ref> validator provided is not a
+       strong validator in the sense defined by
+       <xref target="lastmod.comparison"/>, the condition is false.</li>
+   <li>If the <x:ref>HTTP-date</x:ref> validator provided exactly matches
+       the <x:ref>Last-Modified</x:ref> field value for the selected
+       representation, the condition is true.</li>
+   <li>Otherwise, the condition is false.</li>
+</ol>
+<t>
+   To evaluate a received If-Range header field containing an
+   <x:ref>entity-tag</x:ref>:
+</t>
+<ol>
+   <li>If the <x:ref>entity-tag</x:ref> validator provided exactly matches
+       the <x:ref>ETag</x:ref> field value for the selected representation
+       using the strong comparison function
+       (<xref target="entity.tag.comparison"/>), the condition is true.</li>
+   <li>Otherwise, the condition is false.</li>
+</ol>
+<t>
+   A recipient of an If-Range header field <bcp14>MUST</bcp14> ignore the
+   <x:ref>Range</x:ref> header field if the If-Range condition
+   evaluates to false. Otherwise, the recipient <bcp14>SHOULD</bcp14> process the
+   <x:ref>Range</x:ref> header field as requested.
+</t>
+<t>
+   Note that the If-Range comparison is by exact match, including when the
+   validator is an <x:ref>HTTP-date</x:ref>, and so it
+   differs from the "earlier than or equal to" comparison used when evaluating
+   an <x:ref>If-Unmodified-Since</x:ref> conditional.
+</t>
+</section>
+</section>
+
+<section title="Evaluation of Preconditions" anchor="evaluation">
+
+<section title="When to Evaluate" anchor="when.to.evaluate">
+<t>
+   Except when excluded below, a recipient cache or origin server <bcp14>MUST</bcp14>
+   evaluate received request preconditions after it has successfully performed
+   its normal request checks and just before it would process the request content
+   (if any) or perform the action associated with the request method.
+   A server <bcp14>MUST</bcp14> ignore all received preconditions if its response to the
+   same request without those conditions, prior to processing the request content,
+   would have been a status code other than a <x:ref>2xx (Successful)</x:ref>
+   or <x:ref>412 (Precondition Failed)</x:ref>.
+   In other words, redirects and failures that can be detected before
+   significant processing occurs take precedence over the evaluation
+   of preconditions.
+</t>
+<t>
+   A server that is not the origin server for the target resource and cannot
+   act as a cache for requests on the target resource <bcp14>MUST NOT</bcp14> evaluate the
+   conditional request header fields defined by this specification, and it
+   <bcp14>MUST</bcp14> forward them if the request is forwarded, since the generating
+   client intends that they be evaluated by a server that can provide a
+   current representation.
+   Likewise, a server <bcp14>MUST</bcp14> ignore the conditional request header fields
+   defined by this specification when received with a request method that does
+   not involve the selection or modification of a
+   <x:ref>selected representation</x:ref>, such as CONNECT, OPTIONS, or TRACE.
+</t>
+<t>
+   Note that protocol extensions can modify the conditions under which
+   preconditions are evaluated or the consequences of their evaluation.
+   For example, the immutable cache directive
+   (defined by <xref target="RFC8246"/>) instructs caches to forgo
+   forwarding conditional requests when they hold a fresh response.
+</t>
+<t>
+   Although conditional request header fields are defined as being usable with
+   the HEAD method (to keep HEAD's semantics consistent with those of GET),
+   there is no point in sending a conditional HEAD because a successful
+   response is around the same size as a <x:ref>304 (Not Modified)</x:ref>
+   response and more useful than a <x:ref>412 (Precondition Failed)</x:ref>
+   response.
+</t>
+</section>
+
+<section title="Precedence of Preconditions" anchor="precedence">
+<t>
+   When more than one conditional request header field is present in a request,
+   the order in which the fields are evaluated becomes important. In practice,
+   the fields defined in this document are consistently implemented in a
+   single, logical order, since "lost update" preconditions have more strict
+   requirements than cache validation, a validated cache is more efficient
+   than a partial response, and entity tags are presumed to be more accurate
+   than date validators.
+</t>
+<t>
+   A recipient cache or origin server <bcp14>MUST</bcp14> evaluate the request
+   preconditions defined by this specification in the following order:
+</t>
+<ol>
+   <li anchor="precedence1"><t>When recipient is the origin server and
+     <x:ref>If-Match</x:ref> is present,
+     evaluate the <x:ref>If-Match</x:ref> precondition:</t>
+     <ul>
+       <li>if true, continue to step <xref target="precedence3" format="counter"/></li>
+       <li>if false, respond <x:ref>412 (Precondition Failed)</x:ref> unless
+           it can be determined that the state-changing request has already
+           succeeded (see <xref target="field.if-match"/>)</li>
+     </ul>
+   </li>
+   <li anchor="precedence2"><t>When recipient is the origin server,
+     <x:ref>If-Match</x:ref> is not present, and
+     <x:ref>If-Unmodified-Since</x:ref> is present,
+     evaluate the <x:ref>If-Unmodified-Since</x:ref> precondition:</t>
+     <ul>
+       <li>if true, continue to step <xref target="precedence3" format="counter"/></li>
+       <li>if false, respond <x:ref>412 (Precondition Failed)</x:ref> unless
+           it can be determined that the state-changing request has already
+           succeeded (see <xref target="field.if-unmodified-since"/>)</li>
+     </ul>
+   </li>
+   <li anchor="precedence3"><t>When <x:ref>If-None-Match</x:ref> is present,
+     evaluate the <x:ref>If-None-Match</x:ref> precondition:</t>
+     <ul>
+       <li>if true, continue to step <xref target="precedence5" format="counter"/></li>
+       <li>if false for GET/HEAD, respond <x:ref>304 (Not Modified)</x:ref></li>
+       <li>if false for other methods, respond <x:ref>412 (Precondition Failed)</x:ref></li>
+     </ul>
+   </li>
+   <li anchor="precedence4"><t>When the method is GET or HEAD,
+     <x:ref>If-None-Match</x:ref> is not present, and
+     <x:ref>If-Modified-Since</x:ref> is present,
+     evaluate the <x:ref>If-Modified-Since</x:ref> precondition:</t>
+     <ul>
+       <li>if true, continue to step <xref target="precedence5" format="counter"/></li>
+       <li>if false, respond <x:ref>304 (Not Modified)</x:ref></li>
+     </ul>
+   </li>
+   <li anchor="precedence5"><t>When the method is GET and both
+     <x:ref>Range</x:ref> and <x:ref>If-Range</x:ref> are present,
+     evaluate the <x:ref>If-Range</x:ref> precondition:</t>
+     <ul>
+       <li>if true and the <x:ref>Range</x:ref> is
+           applicable to the <x:ref>selected representation</x:ref>,
+           respond <x:ref>206 (Partial Content)</x:ref></li>
+       <li>otherwise, ignore the <x:ref>Range</x:ref> header field
+           and respond <x:ref>200 (OK)</x:ref></li>
+     </ul>
+   </li>
+   <li anchor="precedencelast"><t>Otherwise,</t>
+     <ul>
+       <li>perform the requested method and
+           respond according to its success or failure.</li>
+     </ul>
+   </li>
+</ol>
+<t>
+   Any extension to HTTP that defines additional conditional request
+   header fields ought to define the order
+   for evaluating such fields in relation to those defined in this document
+   and other conditionals that might be found in practice.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Range Requests" anchor="range.requests">
+<t>
+   Clients often encounter interrupted data
+   transfers as a result of canceled requests or dropped connections. When a
+   client has stored a partial representation, it is desirable to request the
+   remainder of that representation in a subsequent request rather than
+   transfer the entire representation. Likewise, devices with limited local
+   storage might benefit from being able to request only a subset of a larger
+   representation, such as a single page of a very large document, or the
+   dimensions of an embedded image.
+</t>
+<t>
+   Range requests are an <bcp14>OPTIONAL</bcp14> feature
+   of HTTP, designed so that recipients not implementing this feature (or not
+   supporting it for the target resource) can respond as if it is a normal
+   GET request without impacting interoperability. Partial responses are
+   indicated by a distinct status code to not be mistaken for full responses
+   by caches that might not implement the feature.
+</t>
+
+<section title="Range Units" anchor="range.units">
+  <x:anchor-alias value="range-unit"/>
+  <x:anchor-alias value="range unit"/>
+<t>
+   Representation data can be partitioned into subranges when there are
+   addressable structural units inherent to that data's content coding or
+   media type. For example, octet (a.k.a. byte) boundaries are a structural
+   unit common to all representation data, allowing partitions of the data to
+   be identified as a range of bytes at some offset from the start or end of
+   that data.
+</t>
+<t>
+   This general notion of a <x:dfn>range unit</x:dfn> is used
+   in the <x:ref>Accept-Ranges</x:ref> (<xref target="field.accept-ranges"/>)
+   response header field to advertise support for range requests, the
+   <x:ref>Range</x:ref> (<xref target="field.range"/>) request header field
+   to delineate the parts of a representation that are requested, and the
+   <x:ref>Content-Range</x:ref> (<xref target="field.content-range"/>)
+   header field to describe which part of a representation is being
+   transferred.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="range-unit"/>
+  <x:ref>range-unit</x:ref>       = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   All range unit names are case-insensitive and ought to be registered
+   within the "HTTP Range Unit Registry", as defined in
+   <xref target="range.unit.registry"/>.
+</t>
+<t>
+   Range units are intended to be extensible, as described in
+   <xref target="range.unit.extensibility"/>.
+</t>
+
+<section title="Range Specifiers" anchor="range.specifiers">
+  <iref primary="true" item="satisfiable range"/>
+  <iref primary="true" item="unsatisfiable range"/>
+<t>
+   Ranges are expressed in terms of a range unit paired with a set of range
+   specifiers. The range unit name determines what kinds of range-spec
+   are applicable to its own specifiers. Hence, the following grammar is
+   generic: each range unit is expected to specify requirements on when
+   <x:ref>int-range</x:ref>, <x:ref>suffix-range</x:ref>, and
+   <x:ref>other-range</x:ref> are allowed.
+</t>
+<t anchor="rule.ranges-specifier">
+  <x:anchor-alias value="ranges-specifier"/>
+  <x:anchor-alias value="range-set"/>
+  <x:anchor-alias value="range-spec"/>
+   A range request can specify a single range or a set
+   of ranges within a single representation.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="ranges-specifier"/><iref primary="true" item="Grammar" subitem="range-set"/><iref primary="true" item="Grammar" subitem="range-spec"/>
+  <x:ref>ranges-specifier</x:ref> = <x:ref>range-unit</x:ref> "=" <x:ref>range-set</x:ref>
+  <x:ref>range-set</x:ref>        = 1#<x:ref>range-spec</x:ref>
+  <x:ref>range-spec</x:ref>       = <x:ref>int-range</x:ref>
+                   / <x:ref>suffix-range</x:ref>
+                   / <x:ref>other-range</x:ref>
+</sourcecode>
+<t anchor="rule.int-range">
+  <x:anchor-alias value="int-range"/>
+  <x:anchor-alias value="first-pos"/>
+  <x:anchor-alias value="last-pos"/>
+   An <x:ref>int-range</x:ref> is a range expressed as two non-negative
+   integers or as one non-negative integer through to the end of the
+   representation data.
+   The range unit specifies what the integers mean (e.g., they might indicate
+   unit offsets from the beginning, inclusive numbered parts, etc.).
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="int-range"/><iref primary="true" item="Grammar" subitem="first-pos"/><iref primary="true" item="Grammar" subitem="last-pos"/>
+  <x:ref>int-range</x:ref>     = <x:ref>first-pos</x:ref> "-" [ <x:ref>last-pos</x:ref> ]
+  <x:ref>first-pos</x:ref>     = 1*<x:ref>DIGIT</x:ref>
+  <x:ref>last-pos</x:ref>      = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   An <x:ref>int-range</x:ref> is invalid if the
+   <x:ref>last-pos</x:ref> value is present and less than the
+   <x:ref>first-pos</x:ref>.
+</t>
+<t anchor="rule.suffix-range">
+  <x:anchor-alias value="suffix-range"/>
+  <x:anchor-alias value="suffix-length"/>
+   A <x:ref>suffix-range</x:ref> is a range expressed as a suffix of the
+   representation data with the provided non-negative integer maximum length
+   (in range units). In other words, the last N units of the representation
+   data.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="suffix-range"/><iref primary="true" item="Grammar" subitem="suffix-length"/>
+  <x:ref>suffix-range</x:ref>  = "-" <x:ref>suffix-length</x:ref>
+  <x:ref>suffix-length</x:ref> = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t anchor="rule.other-range">
+  <x:anchor-alias value="other-range"/>
+   To provide for extensibility, the <x:ref>other-range</x:ref> rule is a
+   mostly unconstrained grammar that allows application-specific or future
+   range units to define additional range specifiers.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="other-range"/>
+  <x:ref>other-range</x:ref>   = 1*( %x21-2B / %x2D-7E )
+                ; 1*(VCHAR excluding comma)
+</sourcecode>
+<t>
+   A <x:ref>ranges-specifier</x:ref> is invalid if it contains any
+   <x:ref>range-spec</x:ref> that is invalid or undefined for the indicated
+   <x:ref>range-unit</x:ref>.
+</t>
+<t anchor="satisfiable">
+   A valid <x:ref>ranges-specifier</x:ref> is <x:dfn>satisfiable</x:dfn>
+   if it contains at least one <x:ref>range-spec</x:ref> that is
+   satisfiable, as defined by the indicated <x:ref>range-unit</x:ref>.
+   Otherwise, the <x:ref>ranges-specifier</x:ref> is
+   <x:dfn>unsatisfiable</x:dfn>.
+</t>
+</section>
+
+<section title="Byte Ranges" anchor="byte.ranges">
+  <x:anchor-alias value="bytes-unit"/>
+<t>
+   The "bytes" range unit is used to express subranges of a representation
+   data's octet sequence.
+   Each byte range is expressed as an integer range at some offset, relative
+   to either the beginning (<x:ref>int-range</x:ref>) or end
+   (<x:ref>suffix-range</x:ref>) of the representation data.
+   Byte ranges do not use the <x:ref>other-range</x:ref> specifier.
+</t>
+<t>
+   The <x:ref>first-pos</x:ref> value in a bytes <x:ref>int-range</x:ref>
+   gives the offset of the first byte in a range.
+   The <x:ref>last-pos</x:ref> value gives the offset of the last
+   byte in the range; that is, the byte positions specified are inclusive.
+   Byte offsets start at zero.
+</t>
+<t>
+   If the representation data has a content coding applied, each byte range is
+   calculated with respect to the encoded sequence of bytes, not the sequence
+   of underlying bytes that would be obtained after decoding.
+</t>
+<t>
+   Examples of bytes range specifiers:
+</t>
+<ul>
+   <li><t>The first 500 bytes (byte offsets 0-499, inclusive):</t>
+<artwork type="example" x:indent-with="     ">
+bytes=0-499
+</artwork>
+   </li>
+   <li><t>The second 500 bytes (byte offsets 500-999, inclusive):</t>
+<artwork type="example" x:indent-with="     ">
+bytes=500-999
+</artwork>
+  </li>
+</ul>
+<t>
+   A client can limit the number of bytes requested without knowing the size
+   of the <x:ref>selected representation</x:ref>.
+   If the <x:ref>last-pos</x:ref> value is absent, or if the value is
+   greater than or equal to the current length of the representation data, the
+   byte range is interpreted as the remainder of the representation (i.e., the
+   server replaces the value of <x:ref>last-pos</x:ref> with a value that
+   is one less than the current length of the selected representation).
+</t>
+<t>
+   A client can refer to the last N bytes (N &gt; 0) of the selected
+   representation using a <x:ref>suffix-range</x:ref>.
+   If the selected representation is shorter than the specified
+   <x:ref>suffix-length</x:ref>, the entire representation is used.
+</t>
+<t>
+   Additional examples, assuming a representation of length 10000:
+</t>
+<ul>
+   <li><t>The final 500 bytes (byte offsets 9500-9999, inclusive):</t>
+<artwork type="example" x:indent-with="     ">
+bytes=-500
+</artwork>
+  <t>Or:</t>
+<artwork type="example" x:indent-with="     ">
+bytes=9500-
+</artwork>
+   </li>
+   <li><t>The first and last bytes only (bytes 0 and 9999):</t>
+<artwork type="example" x:indent-with="     ">
+bytes=0-0,-1
+</artwork>
+   </li>
+   <li><t>The first, middle, and last 1000 bytes:</t>
+<artwork type="example" x:indent-with="     ">
+bytes= 0-999, 4500-5499, -1000
+</artwork>
+   </li>
+   <li><t>Other valid (but not canonical) specifications of the second 500
+      bytes (byte offsets 500-999, inclusive):</t>
+<artwork type="example" x:indent-with="     ">
+bytes=500-600,601-999
+bytes=500-700,601-999
+</artwork>
+   </li>
+</ul>
+<t>
+   For a <x:ref>GET</x:ref> request, a valid bytes <x:ref>range-spec</x:ref>
+   is <x:ref>satisfiable</x:ref> if it is either:
+</t>
+<ul>
+   <li>an <x:ref>int-range</x:ref> with a <x:ref>first-pos</x:ref> that
+       is less than the current length of the selected representation or</li>
+   <li>a <x:ref>suffix-range</x:ref> with a non-zero
+       <x:ref>suffix-length</x:ref>.</li>
+</ul>
+<t>
+   When a selected representation has zero length, the only
+   <x:ref>satisfiable</x:ref> form of <x:ref>range-spec</x:ref> in a
+   <x:ref>GET</x:ref> request is a <x:ref>suffix-range</x:ref> with a
+   non-zero <x:ref>suffix-length</x:ref>.
+</t>
+<t>
+   In the byte-range syntax, <x:ref>first-pos</x:ref>,
+   <x:ref>last-pos</x:ref>, and <x:ref>suffix-length</x:ref> are
+   expressed as decimal number of octets. Since there is no predefined limit
+   to the length of content, recipients <bcp14>MUST</bcp14> anticipate potentially
+   large decimal numerals and prevent parsing errors due to integer conversion
+   overflows.
+</t>
+</section>
+</section>
+
+<section title="Range" anchor="field.range">
+  <x:anchor-alias value="header.range"/>
+  <iref primary="true" item="Fields" subitem="Range" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Range" x:for-anchor=""/><iref primary="true" item="Range header field" x:for-anchor=""/>
+  <x:anchor-alias value="Range"/>
+<t>
+   The "Range" header field on a GET request modifies the method semantics to
+   request transfer of only one or more subranges of the
+   selected representation data (<xref target="representation.data"/>),
+   rather than the entire <x:ref>selected representation</x:ref>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Range"/>
+  <x:ref>Range</x:ref> = <x:ref>ranges-specifier</x:ref>
+</sourcecode>
+<t>
+   A server <bcp14>MAY</bcp14> ignore the Range header field. However, origin servers and
+   intermediate caches ought to support byte ranges when possible, since they
+   support efficient recovery from partially failed transfers and partial
+   retrieval of large representations.
+</t>
+<t>
+   A server <bcp14>MUST</bcp14> ignore a Range header field received with a request method
+   that is unrecognized or for which range handling is not defined. For this
+   specification, <x:ref>GET</x:ref> is the only method for which range handling
+   is defined.
+</t>
+<t>
+   An origin server <bcp14>MUST</bcp14> ignore a Range header field that contains a range
+   unit it does not understand. A proxy <bcp14>MAY</bcp14> discard a Range header
+   field that contains a range unit it does not understand.
+</t>
+<t>
+   A server that supports range requests <bcp14>MAY</bcp14> ignore or reject a
+   <x:ref>Range</x:ref> header field that contains an invalid
+   <x:ref>ranges-specifier</x:ref> (<xref target="range.specifiers"/>),
+   a <x:ref>ranges-specifier</x:ref> with more than two overlapping ranges,
+   or a set of many small ranges that are not listed in ascending order,
+   since these are indications of either a broken client or a deliberate
+   denial-of-service attack (<xref target="overlapping.ranges"/>).
+   A client <bcp14>SHOULD NOT</bcp14> request multiple ranges that are inherently less
+   efficient to process and transfer than a single range that encompasses the
+   same data.
+</t>
+<t>
+   A server that supports range requests <bcp14>MAY</bcp14> ignore a <x:ref>Range</x:ref>
+   header field when the selected representation has no content
+   (i.e., the selected representation's data is of zero length).
+</t>
+<t>
+   A client that is requesting multiple ranges <bcp14>SHOULD</bcp14> list those ranges in
+   ascending order (the order in which they would typically be received in a
+   complete representation) unless there is a specific need to request a later
+   part earlier. For example, a user agent processing a large representation
+   with an internal catalog of parts might need to request later parts first,
+   particularly if the representation consists of pages stored in reverse
+   order and the user agent wishes to transfer one page at a time.
+</t>
+<t>
+   The Range header field is evaluated after evaluating the precondition header
+   fields defined in <xref target="preconditions"/>, and only if the result in absence
+   of the Range header field would be a <x:ref>200 (OK)</x:ref> response. In
+   other words, Range is ignored when a conditional GET would result in a
+   <x:ref>304 (Not Modified)</x:ref> response.
+</t>
+<t>
+   The If-Range header field (<xref target="field.if-range"/>) can be used as
+   a precondition to applying the Range header field.
+</t>
+<t>
+   If all of the preconditions are true, the server supports the Range header
+   field for the target resource, the received Range field-value contains a
+   valid <x:ref>ranges-specifier</x:ref> with a <x:ref>range-unit</x:ref>
+   supported for that target resource, and that
+   <x:ref>ranges-specifier</x:ref> is <x:ref>satisfiable</x:ref> with respect
+   to the selected representation,
+   the server <bcp14>SHOULD</bcp14> send a <x:ref>206 (Partial Content)</x:ref> response
+   with content containing one or more partial representations
+   that correspond to the satisfiable <x:ref>range-spec</x:ref>(s) requested.
+</t>
+<t>
+   The above does not imply that a server will send all requested ranges.
+   In some cases, it may only be possible (or efficient) to send a portion of
+   the requested ranges first, while expecting the client to re-request the
+   remaining portions later if they are still desired
+   (see <xref target="status.206"/>).
+</t>
+<t>
+   If all of the preconditions are true, the server supports the Range header
+   field for the target resource, the received Range field-value contains a
+   valid <x:ref>ranges-specifier</x:ref>, and either the
+   <x:ref>range-unit</x:ref> is not supported for that target resource or
+   the <x:ref>ranges-specifier</x:ref> is unsatisfiable with respect to
+   the selected representation, the server <bcp14>SHOULD</bcp14> send a
+   <x:ref>416 (Range Not Satisfiable)</x:ref> response.
+</t>
+</section>
+
+<section title="Accept-Ranges" anchor="field.accept-ranges">
+  <x:anchor-alias value="header.accept-ranges"/>
+  <iref primary="true" item="Fields" subitem="Accept-Ranges" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Accept-Ranges" x:for-anchor=""/><iref primary="true" item="Accept-Ranges header field" x:for-anchor=""/>
+  <x:anchor-alias value="Accept-Ranges"/>
+  <x:anchor-alias value="acceptable-ranges"/>
+<t>
+   The "Accept-Ranges" field in a response indicates whether an upstream
+   server supports range requests for the target resource.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Accept-Ranges"/><iref primary="true" item="Grammar" subitem="acceptable-ranges"/>
+  <x:ref>Accept-Ranges</x:ref>     = <x:ref>acceptable-ranges</x:ref>
+  <x:ref>acceptable-ranges</x:ref> = 1#<x:ref>range-unit</x:ref>
+</sourcecode>
+<t>
+   For example, a server that supports
+   <xref target="byte.ranges">byte-range requests</xref> can send the field
+</t>
+<sourcecode type="http-message">
+Accept-Ranges: bytes
+</sourcecode>
+<t>
+   to indicate that it supports byte range requests for that target resource,
+   thereby encouraging its use by the client for future partial requests on
+   the same request path.
+   Range units are defined in <xref target="range.units"/>.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> generate range requests regardless of having received an
+   Accept-Ranges field. The information only provides advice for the sake of
+   improving performance and reducing unnecessary network transfers.
+</t>
+<t>
+   Conversely, a client <bcp14>MUST NOT</bcp14> assume that receiving an Accept-Ranges field
+   means that future range requests will return partial responses. The content might
+   change, the server might only support range requests at certain times or under
+   certain conditions, or a different intermediary might process the next request.
+</t>
+<t>
+   A server that does not support any kind of range request for the target
+   resource <bcp14>MAY</bcp14> send
+</t>
+<sourcecode type="http-message">
+Accept-Ranges: none
+</sourcecode>
+<t>
+   to advise the client not to attempt a range request on the same request path.
+   The range unit "none" is reserved for this purpose.
+</t>
+<t>
+   The Accept-Ranges field <bcp14>MAY</bcp14> be sent in a trailer section, but is preferred
+   to be sent as a header field because the information is particularly useful
+   for restarting large information transfers that have failed in mid-content
+   (before the trailer section is received).
+</t>
+</section>
+
+<section title="Content-Range" anchor="field.content-range">
+  <x:anchor-alias value="header.content-range"/>
+  <iref primary="true" item="Fields" subitem="Content-Range" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-Range" x:for-anchor=""/><iref primary="true" item="Content-Range header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Range"/>
+  <x:anchor-alias value="range-resp"/>
+  <x:anchor-alias value="incl-range"/>
+  <x:anchor-alias value="unsatisfied-range"/>
+  <x:anchor-alias value="complete-length"/>
+<t>
+   The "Content-Range" header field is sent in a single part
+   <x:ref>206 (Partial Content)</x:ref> response to indicate the partial range
+   of the <x:ref>selected representation</x:ref> enclosed as the message content, sent in
+   each part of a multipart 206 response to indicate the range enclosed within
+   each body part (<xref target="multipart.byteranges"/>), and sent in <x:ref>416 (Range Not Satisfiable)</x:ref>
+   responses to provide information about the selected representation.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Content-Range"/><iref primary="true" item="Grammar" subitem="range-resp"/><iref primary="true" item="Grammar" subitem="incl-range"/><iref primary="true" item="Grammar" subitem="unsatisfied-range"/><iref primary="true" item="Grammar" subitem="complete-length"/><iref primary="false" item="Grammar" subitem="first-pos"/><iref primary="false" item="Grammar" subitem="last-pos"/>
+  <x:ref>Content-Range</x:ref>       = <x:ref>range-unit</x:ref> <x:ref>SP</x:ref>
+                        ( <x:ref>range-resp</x:ref> / <x:ref>unsatisfied-range</x:ref> )
+
+  <x:ref>range-resp</x:ref>          = <x:ref>incl-range</x:ref> "/" ( <x:ref>complete-length</x:ref> / "*" )
+  <x:ref>incl-range</x:ref>          = <x:ref>first-pos</x:ref> "-" <x:ref>last-pos</x:ref>
+  <x:ref>unsatisfied-range</x:ref>   = "*/" <x:ref>complete-length</x:ref>
+
+  <x:ref>complete-length</x:ref>     = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   If a <x:ref>206 (Partial Content)</x:ref> response contains a
+   <x:ref>Content-Range</x:ref> header field with a <x:ref>range unit</x:ref>
+   (<xref target="range.units"/>) that the recipient does not understand, the
+   recipient <bcp14>MUST NOT</bcp14> attempt to recombine it with a stored representation.
+   A proxy that receives such a message <bcp14>SHOULD</bcp14> forward it downstream.
+</t>
+<t>
+   Content-Range might also be sent as a request modifier to request a
+   partial PUT, as described in <xref target="partial.PUT"/>, based on private
+   agreements between client and origin server.
+   A server <bcp14>MUST</bcp14> ignore a Content-Range header field received in a request
+   with a method for which Content-Range support is not defined.
+</t>
+<t>
+   For byte ranges, a sender <bcp14>SHOULD</bcp14> indicate the complete length of the
+   representation from which the range has been extracted, unless the complete
+   length is unknown or difficult to determine. An asterisk character ("*") in
+   place of the complete-length indicates that the representation length was
+   unknown when the header field was generated.
+</t>
+<t>
+   The following example illustrates when the complete length of the selected
+   representation is known by the sender to be 1234 bytes:
+</t>
+<sourcecode type="http-message">
+Content-Range: bytes 42-1233/1234
+</sourcecode>
+<t>
+   and this second example illustrates when the complete length is unknown:
+</t>
+<sourcecode type="http-message">
+Content-Range: bytes 42-1233/*
+</sourcecode>
+<t>
+   A Content-Range field value is invalid if it contains a
+   <x:ref>range-resp</x:ref> that has a <x:ref>last-pos</x:ref>
+   value less than its <x:ref>first-pos</x:ref> value, or a
+   <x:ref>complete-length</x:ref> value less than or equal to its
+   <x:ref>last-pos</x:ref> value. The recipient of an invalid
+   <x:ref>Content-Range</x:ref> <bcp14>MUST NOT</bcp14> attempt to recombine the received
+   content with a stored representation.
+</t>
+<t>
+   A server generating a <x:ref>416 (Range Not Satisfiable)</x:ref> response
+   to a byte-range request <bcp14>SHOULD</bcp14> send a Content-Range header field with an
+   <x:ref>unsatisfied-range</x:ref> value, as in the following example:
+</t>
+<sourcecode type="http-message">
+Content-Range: bytes */1234
+</sourcecode>
+<t>
+   The complete-length in a 416 response indicates the current length of the
+   selected representation.
+</t>
+<t>
+   The Content-Range header field has no meaning for status codes that do
+   not explicitly describe its semantic. For this specification, only the
+   <x:ref>206 (Partial Content)</x:ref> and
+   <x:ref>416 (Range Not Satisfiable)</x:ref> status codes describe a meaning
+   for Content-Range.
+</t>
+<t>
+   The following are examples of Content-Range values in which the
+   selected representation contains a total of 1234 bytes:
+</t>
+<ul>
+   <li><t>The first 500 bytes:</t>
+<sourcecode type="http-message">
+Content-Range: bytes 0-499/1234
+</sourcecode>
+   </li>
+   <li><t>The second 500 bytes:</t>
+<sourcecode type="http-message">
+Content-Range: bytes 500-999/1234
+</sourcecode>
+   </li>
+   <li><t>All except for the first 500 bytes:</t>
+<sourcecode type="http-message">
+Content-Range: bytes 500-1233/1234
+</sourcecode>
+   </li>
+   <li><t>The last 500 bytes:</t>
+<sourcecode type="http-message">
+Content-Range: bytes 734-1233/1234
+</sourcecode>
+   </li>
+</ul>
+</section>
+
+<section title="Partial PUT" anchor="partial.PUT">
+  <x:anchor-alias value="partial PUT"/>
+  <iref primary="false" item="Fields" subitem="Content-Range"/><iref primary="false" item="Header Fields" subitem="Content-Range"/><iref primary="false" item="Content-Range header field"/>
+<t>
+   Some origin servers support <x:ref>PUT</x:ref> of a partial representation
+   when the user agent sends a <x:ref>Content-Range</x:ref> header field
+   (<xref target="field.content-range"/>) in the request, though
+   such support is inconsistent and depends on private agreements with
+   user agents. In general, it requests that the state of the
+   <x:ref>target resource</x:ref> be partly replaced with the enclosed content
+   at an offset and length indicated by the Content-Range value, where the
+   offset is relative to the current selected representation.
+</t>
+<t>
+   An origin server <bcp14>SHOULD</bcp14> respond with a <x:ref>400 (Bad Request)</x:ref>
+   status code if it receives <x:ref>Content-Range</x:ref> on a PUT for a
+   target resource that does not support partial PUT requests.
+</t>
+<t>
+   Partial PUT is not backwards compatible with the original definition of PUT.
+   It may result in the content being written as a complete replacement for the
+   current representation.
+</t>
+<t>
+   Partial resource updates are also possible by targeting a separately
+   identified resource with state that overlaps or extends a portion of the
+   larger resource, or by using a different method that has been specifically
+   defined for partial updates (for example, the PATCH method defined in
+   <xref target="RFC5789"/>).
+</t>
+</section>
+
+<section title="Media Type multipart/byteranges" anchor="multipart.byteranges">
+<iref item="Media Type" subitem="multipart/byteranges" primary="true"/>
+<iref item="multipart/byteranges Media Type" primary="true"/>
+<t>
+   When a <x:ref>206 (Partial Content)</x:ref> response message includes the
+   content of multiple ranges, they are transmitted as body parts in a
+   multipart message body (<xref target="RFC2046" x:fmt="," x:sec="5.1"/>)
+   with the media type of "multipart/byteranges".
+</t>
+<t>
+   The "multipart/byteranges" media type includes one or more body parts, each
+   with its own <x:ref>Content-Type</x:ref> and <x:ref>Content-Range</x:ref>
+   fields. The required boundary parameter specifies the boundary string used
+   to separate each body part.
+</t>
+<t>
+  Implementation Notes:
+</t>
+<ol>
+  <li>Additional CRLFs might precede the first boundary string in the body.</li>
+  <li>Although <xref target="RFC2046"/> permits the boundary string to be
+      quoted, some existing implementations handle a quoted boundary
+      string incorrectly.</li>
+  <li>A number of clients and servers were coded to an early draft
+      of the byteranges specification that used a media type of
+      "multipart/x-byteranges",<iref item="multipart/x-byteranges Media Type"/><iref item="Media Type" subitem="multipart/x-byteranges"/>
+      which is almost (but not quite) compatible with this type.</li>
+</ol>
+<t>
+   Despite the name, the "multipart/byteranges" media type is not limited to
+   byte ranges. The following example uses an "exampleunit" range unit:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 206 Partial Content
+Date: Tue, 14 Nov 1995 06:25:24 GMT
+Last-Modified: Tue, 14 July 04:58:08 GMT
+Content-Length: 2331785
+Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
+
+--THIS_STRING_SEPARATES
+Content-Type: video/example
+Content-Range: exampleunit 1.2-4.3/25
+
+...the first range...
+--THIS_STRING_SEPARATES
+Content-Type: video/example
+Content-Range: exampleunit 11.2-14.3/25
+
+...the second range
+--THIS_STRING_SEPARATES--
+</sourcecode>
+<t>
+  The following information serves as the registration form for the
+  "multipart/byteranges" media type.
+</t>
+<dl>
+  <dt>Type name:</dt>
+  <dd>multipart</dd>
+  <dt>Subtype name:</dt>
+  <dd>byteranges</dd>
+  <dt>Required parameters:</dt>
+  <dd>boundary</dd>
+  <dt>Optional parameters:</dt>
+  <dd>N/A</dd>
+  <dt>Encoding considerations:</dt>
+  <dd>only "7bit", "8bit", or "binary" are permitted</dd>
+  <dt>Security considerations:</dt>
+  <dd>see <xref target="security.considerations"/></dd>
+  <dt>Interoperability considerations:</dt>
+  <dd>N/A</dd>
+  <dt>Published specification:</dt>
+  <dd>RFC 9110 (see <xref target="multipart.byteranges"/>)</dd>
+  <dt>Applications that use this media type:</dt>
+  <dd>HTTP components supporting multiple ranges in a single request</dd>
+  <dt>Fragment identifier considerations:</dt>
+  <dd>N/A</dd>
+  <dt>Additional information:</dt>
+  <dd>
+    <dl>
+      <dt>Deprecated alias names for this type:</dt>
+      <dd>N/A</dd>
+      <dt>Magic number(s):</dt>
+      <dd>N/A</dd>
+      <dt>File extension(s):</dt>
+      <dd>N/A</dd>
+      <dt>Macintosh file type code(s):</dt>
+      <dd>N/A</dd>
+    </dl>
+  </dd>
+  <dt>Person and email address to contact for further information:</dt>
+  <dd>See Authors' Addresses section.</dd>
+  <dt>Intended usage:</dt>
+  <dd>COMMON</dd>
+  <dt>Restrictions on usage:</dt>
+  <dd>N/A</dd>
+  <dt>Author:</dt>
+  <dd>See Authors' Addresses section.</dd>
+  <dt>Change controller:</dt>
+  <dd>IESG</dd>
+</dl>
+</section>
+</section>
+
+<section title="Status Codes" anchor="status.codes">
+  <iref item="Status Code"/>
+<t>
+   The status code of a response is a three-digit integer code that describes
+   the result of the request and the semantics of the response, including
+   whether the request was successful and what content is enclosed (if any).
+   All valid status codes are within the range of 100 to 599, inclusive.
+</t>
+<t>
+   The first digit of the status code defines the class of response. The
+   last two digits do not have any categorization role. There are five
+   values for the first digit:
+</t>
+<ul>
+   <li>
+    <x:ref>1xx (Informational)</x:ref>: The request was received, continuing
+    process
+   </li>
+   <li>
+    <x:ref>2xx (Successful)</x:ref>: The request was successfully received,
+    understood, and accepted
+   </li>
+   <li>
+    <x:ref>3xx (Redirection)</x:ref>: Further action needs to be taken in order to
+    complete the request
+   </li>
+   <li>
+    <x:ref>4xx (Client Error)</x:ref>: The request contains bad syntax or cannot
+    be fulfilled
+   </li>
+   <li>
+    <x:ref>5xx (Server Error)</x:ref>: The server failed to fulfill an apparently
+    valid request
+   </li>
+</ul>
+<t>
+   HTTP status codes are extensible. A client is not required to understand
+   the meaning of all registered status codes, though such understanding is
+   obviously desirable. However, a client <bcp14>MUST</bcp14> understand the class of any
+   status code, as indicated by the first digit, and treat an unrecognized
+   status code as being equivalent to the x00 status code of that class.
+</t>
+<t>
+   For example, if a client receives an unrecognized status code of 471,
+   it can see from the first digit that there was something wrong with its
+   request and treat the response as if it had received a
+   <x:ref>400 (Bad Request)</x:ref> status code. The response
+   message will usually contain a representation that explains the status.
+</t>
+<t>
+   Values outside the range 100..599 are invalid. Implementations often use
+   three-digit integer values outside of that range (i.e., 600..999) for
+   internal communication of non-HTTP status (e.g., library errors). A client
+   that receives a response with an invalid status code <bcp14>SHOULD</bcp14> process the
+   response as if it had a <x:ref>5xx (Server Error)</x:ref> status code.
+</t>
+<t anchor="final.interim">
+  <x:anchor-alias value="final"/>
+  <x:anchor-alias value="interim"/>
+  <iref item="Status Codes" subitem="Final"/>
+  <iref item="Status Codes" subitem="Interim"/>
+  <iref item="Status Codes" subitem="Informational"/>
+  A single request can have multiple associated responses: zero or more
+  <x:dfn>interim</x:dfn> (non-final) responses with status codes in the
+  "informational" (<x:ref>1xx</x:ref>) range, followed by exactly one
+  <x:dfn>final</x:dfn> response with a status code in one of the other ranges.
+</t>
+
+<section title="Overview of Status Codes" anchor="overview.of.status.codes">
+<t>
+   The status codes listed below are defined in this specification.
+   The reason phrases listed here are only recommendations — they can be
+   replaced by local equivalents or left out altogether without affecting the
+   protocol.
+</t>
+<t>
+   Responses with status codes that are defined as heuristically cacheable
+   (e.g., 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, and 501 in this
+   specification) can be reused by a cache with heuristic expiration unless
+   otherwise indicated by the method definition or explicit cache controls
+   <xref target="CACHING"/>; all other status codes are not heuristically cacheable.
+</t>
+<t>
+   Additional status codes, outside the scope of this specification, have been
+   specified for use in HTTP. All such status codes ought to be registered
+   within the "Hypertext Transfer Protocol (HTTP) Status Code Registry",
+   as described in <xref target="status.code.extensibility"/>.
+</t>
+</section>
+
+<section title="Informational 1xx" anchor="status.1xx">
+  <x:anchor-alias value="1xx"/>
+  <x:anchor-alias value="1xx (Informational)"/>
+  <iref primary="true" item="1xx Informational (status code class)" x:for-anchor=""/>
+  <iref primary="true" item="Status Codes Classes" subitem="1xx Informational" x:for-anchor=""/>
+<t>
+   The 1xx (Informational) class of status code indicates an
+   interim response for communicating connection status or request progress
+   prior to completing the requested action and sending a final response.
+   Since HTTP/1.0 did not define any 1xx status codes, a server <bcp14>MUST NOT</bcp14> send
+   a 1xx response to an HTTP/1.0 client.
+</t>
+<t>
+   A 1xx response is terminated by the end of the header section;
+   it cannot contain content or trailers.
+</t>
+<t>
+   A client <bcp14>MUST</bcp14> be able to parse one or more 1xx responses received
+   prior to a final response, even if the client does not expect one.
+   A user agent <bcp14>MAY</bcp14> ignore unexpected 1xx responses.
+</t>
+<t>
+   A proxy <bcp14>MUST</bcp14> forward 1xx responses unless the proxy itself
+   requested the generation of the 1xx response. For example, if a
+   proxy adds an "Expect: 100-continue" header field when it forwards a request,
+   then it need not forward the corresponding <x:ref>100 (Continue)</x:ref>
+   response(s).
+</t>
+
+<section title="100 Continue" anchor="status.100">
+  <iref primary="true" item="100 Continue (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="100 (Continue)"/>
+<t>
+   The 100 (Continue) status code indicates that the initial
+   part of a request has been received and has not yet been rejected by the
+   server. The server intends to send a final response after the request has
+   been fully received and acted upon.
+</t>
+<t>
+   When the request contains an <x:ref>Expect</x:ref> header field that
+   includes a <x:ref>100-continue</x:ref> expectation, the 100 response
+   indicates that the server wishes to receive the request content,
+   as described in <xref target="field.expect"/>.  The client
+   ought to continue sending the request and discard the 100 response.
+</t>
+<t>
+   If the request did not contain an <x:ref>Expect</x:ref> header field
+   containing the <x:ref>100-continue</x:ref> expectation,
+   the client can simply discard this interim response.
+</t>
+</section>
+
+<section title="101 Switching Protocols" anchor="status.101">
+  <iref primary="true" item="101 Switching Protocols (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="101 (Switching Protocols)"/>
+<t>
+   The 101 (Switching Protocols) status code indicates that the
+   server understands and is willing to comply with the client's request,
+   via the <x:ref>Upgrade</x:ref> header field (<xref target="field.upgrade"/>), for
+   a change in the application protocol being used on this connection.
+   The server <bcp14>MUST</bcp14> generate an Upgrade header field in the response that
+   indicates which protocol(s) will be in effect after this response.
+</t>
+<t>
+   It is assumed that the server will only agree to switch protocols when
+   it is advantageous to do so. For example, switching to a newer version of
+   HTTP might be advantageous over older versions, and switching to a
+   real-time, synchronous protocol might be advantageous when delivering
+   resources that use such features.
+</t>
+</section>
+</section>
+
+<section title="Successful 2xx" anchor="status.2xx">
+  <x:anchor-alias value="2xx"/>
+  <x:anchor-alias value="2xx (Successful)"/>
+  <iref primary="true" item="2xx Successful (status code class)" x:for-anchor=""/>
+  <iref primary="true" item="Status Codes Classes" subitem="2xx Successful" x:for-anchor=""/>
+<t>
+   The 2xx (Successful) class of status code indicates that
+   the client's request was successfully received, understood, and accepted.
+</t>
+
+<section title="200 OK" anchor="status.200">
+  <iref primary="true" item="200 OK (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="200 (OK)"/>
+<t>
+   The 200 (OK) status code indicates that the request has
+   succeeded. The content sent in a 200 response depends on the request
+   method. For the methods defined by this specification, the intended meaning
+   of the content can be summarized as:
+</t>
+<table align="left">
+  <thead>
+    <tr>
+      <th>Request Method</th>
+      <th>Response content is a representation of:</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>GET</td>
+      <td>the <x:ref>target resource</x:ref></td>
+    </tr>
+    <tr>
+      <td>HEAD</td>
+      <td>the <x:ref>target resource</x:ref>, like GET, but without
+          transferring the representation data</td>
+    </tr>
+    <tr>
+      <td>POST</td>
+      <td>the status of, or results obtained from, the action</td>
+    </tr>
+    <tr>
+      <td>PUT, DELETE</td>
+      <td>the status of the action</td>
+    </tr>
+    <tr>
+      <td>OPTIONS</td>
+      <td>communication options for the target resource</td>
+    </tr>
+    <tr>
+      <td>TRACE</td>
+      <td>the request message as received by the server returning the
+          trace</td>
+    </tr>
+  </tbody>
+</table>
+<t>
+   Aside from responses to CONNECT, a 200 response is expected to contain
+   message content unless the message framing explicitly indicates that the
+   content has zero length. If some aspect of the request indicates a
+   preference for no content upon success, the origin server ought to send a
+   <x:ref>204 (No Content)</x:ref> response instead.
+   For CONNECT, there is no content because the successful result is a
+   tunnel, which begins immediately after the 200 response header section.
+</t>
+<t>
+   A 200 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+<t>
+   In 200 responses to GET or HEAD, an origin server <bcp14>SHOULD</bcp14> send any
+   available validator fields (<xref target="response.validator"/>) for the
+   <x:ref>selected representation</x:ref>, with both a strong entity tag and
+   a <x:ref>Last-Modified</x:ref> date being preferred.
+</t>
+<t>
+   In 200 responses to state-changing methods, any validator fields
+   (<xref target="response.validator"/>) sent in the response convey the
+   current validators for the new representation formed as a result of
+   successfully applying the request semantics. Note that the PUT method
+   (<xref target="PUT"/>) has additional requirements that might preclude
+   sending such validators.
+</t>
+</section>
+
+<section title="201 Created" anchor="status.201">
+  <iref primary="true" item="201 Created (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="201 (Created)"/>
+<t>
+   The 201 (Created) status code indicates that the request has
+   been fulfilled and has resulted in one or more new resources being created.
+   The primary resource created by the request is identified by either a
+   <x:ref>Location</x:ref> header field in the response or, if no
+   <x:ref>Location</x:ref> header field is received, by the target URI.
+</t>
+<t>
+   The 201 response content typically describes and links to the resource(s)
+   created. Any validator fields (<xref target="response.validator"/>)
+   sent in the response convey the current validators for a new
+   representation created by the request. Note that the PUT method
+   (<xref target="PUT"/>) has additional requirements that might preclude
+   sending such validators.
+</t>
+</section>
+
+<section title="202 Accepted" anchor="status.202">
+  <iref primary="true" item="202 Accepted (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="202 (Accepted)"/>
+<t>
+   The 202 (Accepted) status code indicates that the request
+   has been accepted for processing, but the processing has not been
+   completed. The request might or might not eventually be acted upon, as it
+   might be disallowed when processing actually takes place. There is no
+   facility in HTTP for re-sending a status code from an asynchronous
+   operation.
+</t>
+<t>
+   The 202 response is intentionally noncommittal. Its purpose is to
+   allow a server to accept a request for some other process (perhaps a
+   batch-oriented process that is only run once per day) without
+   requiring that the user agent's connection to the server persist
+   until the process is completed. The representation sent with this
+   response ought to describe the request's current status and point to
+   (or embed) a status monitor that can provide the user with an estimate of
+   when the request will be fulfilled.
+</t>
+</section>
+
+<section title="203 Non-Authoritative Information" anchor="status.203">
+  <iref primary="true" item="203 Non-Authoritative Information (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="203 (Non-Authoritative Information)"/>
+<t>
+   The 203 (Non-Authoritative Information) status code
+   indicates that the request was successful but the enclosed content has been
+   modified from that of the origin server's <x:ref>200 (OK)</x:ref> response
+   by a transforming proxy (<xref target="message.transformations"/>). This status code allows the
+   proxy to notify recipients when a transformation has been applied, since
+   that knowledge might impact later decisions regarding the content. For
+   example, future cache validation requests for the content might only be
+   applicable along the same request path (through the same proxies).
+</t>
+<t>
+   A 203 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+</section>
+
+<section title="204 No Content" anchor="status.204">
+  <iref primary="true" item="204 No Content (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="204 (No Content)"/>
+<t>
+   The 204 (No Content) status code indicates that the server
+   has successfully fulfilled the request and that there is no additional
+   content to send in the response content. Metadata in the response
+   header fields refer to the <x:ref>target resource</x:ref> and its
+   <x:ref>selected representation</x:ref> after the requested action was applied.
+</t>
+<t>
+   For example, if a 204 status code is received in response to a PUT
+   request and the response contains an <x:ref>ETag</x:ref> field, then
+   the PUT was successful and the ETag field value contains the entity tag for
+   the new representation of that target resource.
+</t>
+<t>
+   The 204 response allows a server to indicate that the action has been
+   successfully applied to the target resource, while implying that the
+   user agent does not need to traverse away from its current "document view"
+   (if any).  The server assumes that the user agent will provide some
+   indication of the success to its user, in accord with its own interface,
+   and apply any new or updated metadata in the response to its active
+   representation.
+</t>
+<t>
+   For example, a 204 status code is commonly used with document editing
+   interfaces corresponding to a "save" action, such that the document
+   being saved remains available to the user for editing. It is also
+   frequently used with interfaces that expect automated data transfers
+   to be prevalent, such as within distributed version control systems.
+</t>
+<t>
+   A 204 response is terminated by the end of the header section;
+   it cannot contain content or trailers.
+</t>
+<t>
+   A 204 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+</section>
+
+<section title="205 Reset Content" anchor="status.205">
+  <iref primary="true" item="205 Reset Content (status code)" x:for-anchor=""/>
+<t>
+   The 205 (Reset Content) status code indicates that the
+   server has fulfilled the request and desires that the user agent reset the
+   "document view", which caused the request to be sent, to its original state
+   as received from the origin server.
+</t>
+<t>
+   This response is intended to support a common data entry use case where
+   the user receives content that supports data entry (a form, notepad,
+   canvas, etc.), enters or manipulates data in that space, causes the entered
+   data to be submitted in a request, and then the data entry mechanism is
+   reset for the next entry so that the user can easily initiate another
+   input action.
+</t>
+<t>
+   Since the 205 status code implies that no additional content will be
+   provided, a server <bcp14>MUST NOT</bcp14> generate content in a 205 response.
+</t>
+</section>
+
+<section title="206 Partial Content" anchor="status.206">
+  <iref primary="true" item="206 Partial Content (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="206"/>
+  <x:anchor-alias value="206 (Partial Content)"/>
+<t>
+   The 206 (Partial Content) status code indicates that the
+   server is successfully fulfilling a range request for the target resource
+   by transferring one or more parts of the
+   <x:ref>selected representation</x:ref>.
+</t>
+<t>
+   A server that supports range requests (<xref target="range.requests"/>) will
+   usually attempt to satisfy all of the requested ranges, since sending
+   less data will likely result in another client request for the remainder.
+   However, a server might want to send only a subset of the data requested
+   for reasons of its own, such as temporary unavailability, cache efficiency,
+   load balancing, etc. Since a 206 response is self-descriptive, the client
+   can still understand a response that only partially satisfies its range
+   request.
+</t>
+<t>
+   A client <bcp14>MUST</bcp14> inspect a 206 response's <x:ref>Content-Type</x:ref> and
+   <x:ref>Content-Range</x:ref> field(s) to determine what parts are enclosed
+   and whether additional requests are needed.
+</t>
+<t>
+   A server that generates a 206 response <bcp14>MUST</bcp14> generate the following
+   header fields, in addition to those required in the subsections below,
+   if the field would
+   have been sent in a <x:ref>200 (OK)</x:ref> response to the same request:
+   <x:ref>Date</x:ref>, <x:ref>Cache-Control</x:ref>, <x:ref>ETag</x:ref>,
+   <x:ref>Expires</x:ref>, <x:ref>Content-Location</x:ref>, and
+   <x:ref>Vary</x:ref>.
+</t>
+<t>
+   A <x:ref>Content-Length</x:ref> header field present in a 206 response
+   indicates the number of octets in the content of this message, which is
+   usually not the complete length of the selected representation.
+   Each <x:ref>Content-Range</x:ref> header field includes information about the
+   selected representation's complete length.
+</t>
+<t>
+   A sender that generates a 206 response to a request with an <x:ref>If-Range</x:ref>
+   header field <bcp14>SHOULD NOT</bcp14> generate other representation header
+   fields beyond those required because the client
+   already has a prior response containing those header fields.
+   Otherwise, a sender <bcp14>MUST</bcp14> generate all of the representation header
+   fields that would have been sent in a <x:ref>200 (OK)</x:ref> response
+   to the same request.
+</t>
+<t>
+   A 206 response is heuristically cacheable; i.e., unless otherwise indicated by
+   explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+
+<section title="Single Part" anchor="partial.single">
+<t>
+   If a single part is being transferred, the server generating the 206
+   response <bcp14>MUST</bcp14> generate a <x:ref>Content-Range</x:ref> header field,
+   describing what range of the selected representation is enclosed, and a
+   content consisting of the range. For example:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 206 Partial Content
+Date: Wed, 15 Nov 1995 06:25:24 GMT
+Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+Content-Range: bytes 21010-47021/47022
+Content-Length: 26012
+Content-Type: image/gif
+
+... 26012 bytes of partial image data ...
+</sourcecode>
+</section>
+
+<section title="Multiple Parts" anchor="partial.multipart">
+<t>
+   If multiple parts are being transferred, the server generating the 206
+   response <bcp14>MUST</bcp14> generate "multipart/byteranges" content, as defined
+   in <xref target="multipart.byteranges"/>, and a
+   <x:ref>Content-Type</x:ref> header field containing the
+   "multipart/byteranges" media type and its required boundary parameter.
+   To avoid confusion with single-part responses, a server <bcp14>MUST NOT</bcp14> generate
+   a <x:ref>Content-Range</x:ref> header field in the HTTP header section of a
+   multiple part response (this field will be sent in each part instead).
+</t>
+<t>
+   Within the header area of each body part in the multipart content, the
+   server <bcp14>MUST</bcp14> generate a <x:ref>Content-Range</x:ref> header field
+   corresponding to the range being enclosed in that body part.
+   If the selected representation would have had a <x:ref>Content-Type</x:ref>
+   header field in a <x:ref>200 (OK)</x:ref> response, the server <bcp14>SHOULD</bcp14>
+   generate that same <x:ref>Content-Type</x:ref> header field in the header area of
+   each body part. For example:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 206 Partial Content
+Date: Wed, 15 Nov 1995 06:25:24 GMT
+Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+Content-Length: 1741
+Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
+
+--THIS_STRING_SEPARATES
+Content-Type: application/pdf
+Content-Range: bytes 500-999/8000
+
+...the first range...
+--THIS_STRING_SEPARATES
+Content-Type: application/pdf
+Content-Range: bytes 7000-7999/8000
+
+...the second range
+--THIS_STRING_SEPARATES--
+</sourcecode>
+<t>
+   When multiple ranges are requested, a server <bcp14>MAY</bcp14> coalesce any of the
+   ranges that overlap, or that are separated by a gap that is smaller than the
+   overhead of sending multiple parts, regardless of the order in which the
+   corresponding range-spec appeared in the received <x:ref>Range</x:ref>
+   header field. Since the typical overhead between each part of a
+   "multipart/byteranges" is around 80 bytes, depending on the selected
+   representation's media type and the chosen boundary parameter length, it
+   can be less efficient to transfer many small disjoint parts than it is to
+   transfer the entire selected representation.
+</t>
+<t>
+   A server <bcp14>MUST NOT</bcp14> generate a multipart response to a request for a single
+   range, since a client that does not request multiple parts might not
+   support multipart responses. However, a server <bcp14>MAY</bcp14> generate a
+   "multipart/byteranges" response with only a single body part if multiple
+   ranges were requested and only one range was found to be satisfiable or
+   only one range remained after coalescing.
+   A client that cannot process a "multipart/byteranges" response <bcp14>MUST NOT</bcp14>
+   generate a request that asks for multiple ranges.
+</t>
+<t>
+   A server that generates a multipart response <bcp14>SHOULD</bcp14> send
+   the parts in the same order that the corresponding range-spec appeared
+   in the received <x:ref>Range</x:ref> header field, excluding those ranges
+   that were deemed unsatisfiable or that were coalesced into other ranges.
+   A client that receives a multipart response <bcp14>MUST</bcp14> inspect the
+   <x:ref>Content-Range</x:ref> header field present in each body part in
+   order to determine which range is contained in that body part; a client
+   cannot rely on receiving the same ranges that it requested, nor the same
+   order that it requested.
+</t>
+</section>
+
+<section title="Combining Parts" anchor="combining.byte.ranges">
+<t>
+   A response might transfer only a subrange of a representation if the
+   connection closed prematurely or if the request used one or more Range
+   specifications.  After several such transfers, a client might have
+   received several ranges of the same representation.  These ranges can only
+   be safely combined if they all have in common the same strong validator
+   (<xref target="weak.and.strong.validators"/>).
+</t>
+<t>
+   A client that has received multiple partial responses to GET requests on a
+   target resource <bcp14>MAY</bcp14> combine those responses into a larger continuous
+   range if they share the same strong validator.
+</t>
+<t>
+   If the most recent response is an incomplete <x:ref>200 (OK)</x:ref>
+   response, then the header fields of that response are used for any
+   combined response and replace those of the matching stored responses.
+</t>
+<t>
+   If the most recent response is a <x:ref>206 (Partial Content)</x:ref>
+   response and at least one of the matching stored responses is a
+   <x:ref>200 (OK)</x:ref>, then the combined response header fields consist
+   of the most recent 200 response's header fields. If all of the matching
+   stored responses are 206 responses, then the stored response with the most
+   recent header fields is used as the source of header fields for the
+   combined response, except that the client <bcp14>MUST</bcp14> use other header fields
+   provided in the new response, aside from <x:ref>Content-Range</x:ref>, to
+   replace all instances of the corresponding header fields in the stored
+   response.
+</t>
+<t>
+   The combined response content consists of the union of partial content
+   ranges within the new response and all of the matching stored responses.
+   If the union consists of the entire range of the representation, then the
+   client <bcp14>MUST</bcp14> process the combined response as if it were a complete
+   <x:ref>200 (OK)</x:ref> response, including a <x:ref>Content-Length</x:ref>
+   header field that reflects the complete length.
+   Otherwise, the client <bcp14>MUST</bcp14> process the set of continuous ranges as one of
+   the following:
+   an incomplete <x:ref>200 (OK)</x:ref> response if the combined response is
+   a prefix of the representation,
+   a single <x:ref>206 (Partial Content)</x:ref> response containing
+   "multipart/byteranges" content, or
+   multiple <x:ref>206 (Partial Content)</x:ref> responses, each with one
+   continuous range that is indicated by a <x:ref>Content-Range</x:ref> header
+   field.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Redirection 3xx" anchor="status.3xx">
+  <x:anchor-alias value="3xx"/>
+  <x:anchor-alias value="3xx (Redirection)"/>
+  <iref primary="true" item="3xx Redirection (status code class)" x:for-anchor=""/>
+  <iref primary="true" item="Status Codes Classes" subitem="3xx Redirection" x:for-anchor=""/>
+<t>
+   The 3xx (Redirection) class of status code indicates that
+   further action needs to be taken by the user agent in order to fulfill the
+   request. There are several types of redirects:
+</t>
+<ol>
+  <li>
+    Redirects that indicate this resource might be available at a
+    different URI, as provided by the <x:ref>Location</x:ref> header field,
+    as in the status codes <x:ref>301 (Moved Permanently)</x:ref>,
+    <x:ref>302 (Found)</x:ref>, <x:ref>307 (Temporary Redirect)</x:ref>, and
+    <x:ref>308 (Permanent Redirect)</x:ref>.
+  </li>
+  <li>
+    Redirection that offers a choice among matching resources capable
+    of representing this resource, as in the
+    <x:ref>300 (Multiple Choices)</x:ref> status code.
+  </li>
+  <li>
+    Redirection to a different resource, identified by the
+    <x:ref>Location</x:ref> header field, that can represent an indirect
+    response to the request, as in the <x:ref>303 (See Other)</x:ref>
+    status code.
+  </li>
+  <li>
+    Redirection to a previously stored result, as in the
+    <x:ref>304 (Not Modified)</x:ref> status code.
+  </li>
+</ol>
+<aside>
+  <t>
+    <x:h>Note:</x:h> In HTTP/1.0, the status codes <x:ref>301 (Moved Permanently)</x:ref>
+    and <x:ref>302 (Found)</x:ref> were originally defined as method-preserving
+    (<xref target="HTTP10" x:fmt="," x:sec="9.3"/>) to match their implementation
+    at CERN; <x:ref>303 (See Other)</x:ref> was defined for a redirection that
+    changed its method to GET. However, early user agents split on whether to
+    redirect POST requests as POST (according to then-current specification)
+    or as GET (the safer alternative when redirected to a different site).
+    Prevailing practice eventually converged on changing the method to GET.
+    <x:ref>307 (Temporary Redirect)</x:ref> and
+    <x:ref>308 (Permanent Redirect)</x:ref> <xref target="RFC7538"/> were
+    later added to unambiguously indicate method-preserving redirects, and
+    status codes <x:ref>301</x:ref> and <x:ref>302</x:ref> have been adjusted to allow a POST
+    request to be redirected as GET.
+  </t>
+</aside>
+<t>
+   If a <x:ref>Location</x:ref> header field
+   (<xref target="field.location"/>) is provided, the user agent <bcp14>MAY</bcp14>
+   automatically redirect its request to the URI referenced by the Location
+   field value, even if the specific status code is not understood.
+   Automatic redirection needs to be done with care for methods not known to be
+   <x:ref>safe</x:ref>, as defined in <xref target="safe.methods"/>, since
+   the user might not wish to redirect an unsafe request.
+</t>
+<t>
+   When automatically following a redirected request, the user agent <bcp14>SHOULD</bcp14>
+   resend the original request message with the following modifications:
+</t>
+<ol>
+  <li>
+    <t>
+      Replace the target URI with the URI referenced by the redirection response's
+      <x:ref>Location</x:ref> header field value after resolving it relative to the original
+      request's target URI.
+    </t>
+  </li>
+  <li>
+    <t>
+      Remove header fields that were automatically generated by the implementation,
+      replacing them with updated values as appropriate to the new request. This
+      includes:
+    </t>
+    <ol>
+       <li>Connection-specific header fields (see <xref target="field.connection"/>),</li>
+       <li>Header fields specific to the client's proxy configuration,
+       including (but not limited to) <x:ref>Proxy-Authorization</x:ref>,</li>
+       <li>Origin-specific header fields (if any), including (but not
+       limited to) <x:ref>Host</x:ref>,</li>
+       <li>Validating header fields that were added by the implementation's
+       cache (e.g., <x:ref>If-None-Match</x:ref>,
+       <x:ref>If-Modified-Since</x:ref>), and</li>
+       <li>Resource-specific header fields, including (but not limited to)
+       <x:ref>Referer</x:ref>, Origin,
+       <x:ref>Authorization</x:ref>, and Cookie.</li>
+     </ol>
+  </li>
+  <li>
+    <t>
+      Consider removing header fields that were not automatically generated by the
+      implementation (i.e., those present in the request because they were added
+      by the calling context) where there are security implications; this
+      includes but is not limited to <x:ref>Authorization</x:ref> and Cookie.
+    </t>
+  </li>
+  <li>
+    <t>
+      Change the request method according to the redirecting status code's
+      semantics, if applicable.
+    </t>
+  </li>
+  <li>
+    <t>
+      If the request method has been changed to GET or HEAD, remove
+      content-specific header fields, including (but not limited to)
+      <x:ref>Content-Encoding</x:ref>,
+      <x:ref>Content-Language</x:ref>, <x:ref>Content-Location</x:ref>,
+      <x:ref>Content-Type</x:ref>, <x:ref>Content-Length</x:ref>,
+      Digest, <x:ref>Last-Modified</x:ref>.
+    </t>
+  </li>
+</ol>
+<t>
+   A client <bcp14>SHOULD</bcp14> detect and intervene in cyclical redirections (i.e.,
+   "infinite" redirection loops).
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> An earlier version of this specification recommended a
+    maximum of five redirections (<xref target="RFC2068" x:fmt="," x:sec="10.3"/>).
+    Content developers need to be aware that some clients might
+    implement such a fixed limitation.
+  </t>
+</aside>
+
+<section title="300 Multiple Choices" anchor="status.300">
+  <iref primary="true" item="300 Multiple Choices (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="300 (Multiple Choices)"/>
+<t>
+   The 300 (Multiple Choices) status code indicates that the
+   <x:ref>target resource</x:ref> has more than one representation, each with
+   its own more specific identifier, and information about the alternatives is
+   being provided so that the user (or user agent) can select a preferred
+   representation by redirecting its request to one or more of those
+   identifiers. In other words, the server desires that the user agent engage
+   in reactive negotiation to select the most appropriate representation(s)
+   for its needs (<xref target="content.negotiation"/>).
+</t>
+<t>
+   If the server has a preferred choice, the server <bcp14>SHOULD</bcp14> generate a
+   <x:ref>Location</x:ref> header field containing a preferred choice's URI
+   reference. The user agent <bcp14>MAY</bcp14> use the Location field value for automatic
+   redirection.
+</t>
+<t>
+   For request methods other than HEAD, the server <bcp14>SHOULD</bcp14> generate content
+   in the 300 response containing a list of representation metadata and URI
+   reference(s) from which the user or user agent can choose the one most
+   preferred. The user agent <bcp14>MAY</bcp14> make a selection from that list
+   automatically if it understands the provided media type. A specific format
+   for automatic selection is not defined by this specification because HTTP
+   tries to remain orthogonal to the definition of its content.
+   In practice, the representation is provided in some easily parsed format
+   believed to be acceptable to the user agent, as determined by shared design
+   or content negotiation, or in some commonly accepted hypertext format.
+</t>
+<t>
+   A 300 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+<aside>
+  <t>
+   <x:h>Note:</x:h> The original proposal for the 300 status code defined the URI header field as
+   providing a list of alternative representations, such that it would be
+   usable for 200, 300, and 406 responses and be transferred in responses to
+   the HEAD method. However, lack of deployment and disagreement over syntax
+   led to both URI and Alternates (a subsequent proposal) being dropped from
+   this specification. It is possible to communicate the list as a
+   Link header field value <xref target="RFC8288"/> whose members have a relationship of
+   "alternate", though deployment is a chicken-and-egg problem.
+  </t>
+</aside>
+</section>
+
+<section title="301 Moved Permanently" anchor="status.301">
+  <iref primary="true" item="301 Moved Permanently (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="301"/>
+  <x:anchor-alias value="301 (Moved Permanently)"/>
+<t>
+   The 301 (Moved Permanently) status code indicates that the
+   <x:ref>target resource</x:ref> has been assigned a new permanent URI and
+   any future references to this resource ought to use one of the enclosed
+   URIs. The server is suggesting that a user agent with link-editing capability
+   can permanently replace references to the target URI with one of the
+   new references sent by the server. However, this suggestion is usually
+   ignored unless the user agent is actively editing references
+   (e.g., engaged in authoring content), the connection is secured, and
+   the origin server is a trusted authority for the content being edited.
+</t>
+<t>
+   The server <bcp14>SHOULD</bcp14> generate a <x:ref>Location</x:ref> header field in the
+   response containing a preferred URI reference for the new permanent URI.
+   The user agent <bcp14>MAY</bcp14> use the Location field value for automatic redirection.
+   The server's response content usually contains a short hypertext note with
+   a hyperlink to the new URI(s).
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> For historical reasons, a user agent <bcp14>MAY</bcp14> change the
+    request method from POST to GET for the subsequent request. If this
+    behavior is undesired, the <x:ref>308 (Permanent Redirect)</x:ref>
+    status code can be used instead.
+  </t>
+</aside>
+<t>
+   A 301 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+</section>
+
+<section title="302 Found" anchor="status.302">
+  <iref primary="true" item="302 Found (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="302"/>
+  <x:anchor-alias value="302 (Found)"/>
+<t>
+   The 302 (Found) status code indicates that the target
+   resource resides temporarily under a different URI. Since the redirection
+   might be altered on occasion, the client ought to continue to use the
+   target URI for future requests.
+</t>
+<t>
+   The server <bcp14>SHOULD</bcp14> generate a <x:ref>Location</x:ref> header field in the
+   response containing a URI reference for the different URI.
+   The user agent <bcp14>MAY</bcp14> use the Location field value for automatic redirection.
+   The server's response content usually contains a short hypertext note with
+   a hyperlink to the different URI(s).
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> For historical reasons, a user agent <bcp14>MAY</bcp14> change the
+    request method from POST to GET for the subsequent request. If this
+    behavior is undesired, the <x:ref>307 (Temporary Redirect)</x:ref>
+    status code can be used instead.
+  </t>
+</aside>
+</section>
+
+<section title="303 See Other" anchor="status.303">
+  <iref primary="true" item="303 See Other (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="303 (See Other)"/>
+<t>
+   The 303 (See Other) status code indicates that the server is
+   redirecting the user agent to a different resource, as indicated by a URI
+   in the <x:ref>Location</x:ref> header field, which is intended to provide
+   an indirect response to the original request. A user agent can perform a
+   retrieval request targeting that URI (a GET or HEAD request if using HTTP),
+   which might also be redirected, and present the eventual result as an
+   answer to the original request. Note that the new URI in the Location
+   header field is not considered equivalent to the target URI.
+</t>
+<t>
+   This status code is applicable to any HTTP method.  It is
+   primarily used to allow the output of a POST action to redirect
+   the user agent to a different resource, since doing so provides the
+   information corresponding to the POST response as a resource that
+   can be separately identified, bookmarked, and cached.
+</t>
+<t>
+   A 303 response to a GET request indicates that the origin server does not
+   have a representation of the <x:ref>target resource</x:ref> that can be
+   transferred by the server over HTTP. However, the
+   <x:ref>Location</x:ref> field value refers to a resource that is
+   descriptive of the target resource, such that making a retrieval request
+   on that other resource might result in a representation that is useful to
+   recipients without implying that it represents the original target resource.
+   Note that answers to the questions of what can be represented, what
+   representations are adequate, and what might be a useful description are
+   outside the scope of HTTP.
+</t>
+<t>
+   Except for responses to a HEAD request, the representation of a 303
+   response ought to contain a short hypertext note with a hyperlink to the
+   same URI reference provided in the <x:ref>Location</x:ref> header field.
+</t>
+</section>
+
+<section title="304 Not Modified" anchor="status.304">
+  <iref primary="true" item="304 Not Modified (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="304"/>
+  <x:anchor-alias value="304 (Not Modified)"/>
+<t>
+   The 304 (Not Modified) status code indicates that a
+   conditional GET or HEAD request has been
+   received and would have resulted in a <x:ref>200 (OK)</x:ref> response
+   if it were not for the fact that the condition evaluated to false.
+   In other words, there is no need for the server to transfer a
+   representation of the target resource because the request indicates that
+   the client, which made the request conditional, already has a valid
+   representation; the server is therefore redirecting the client to make
+   use of that stored representation as if it were the content of a
+   <x:ref>200 (OK)</x:ref> response.
+</t>
+<t>
+   The server generating a 304 response <bcp14>MUST</bcp14> generate any of the following
+   header fields that would have been sent in a <x:ref>200 (OK)</x:ref>
+   response to the same request:
+</t>
+<ul>
+  <li>
+    <x:ref>Content-Location</x:ref>, <x:ref>Date</x:ref>, <x:ref>ETag</x:ref>,
+    and <x:ref>Vary</x:ref>
+  </li>
+  <li>
+    <x:ref>Cache-Control</x:ref> and <x:ref>Expires</x:ref> (see
+    <xref target="CACHING"/>)
+  </li>
+</ul>
+<t>
+   Since the goal of a 304 response is to minimize information transfer
+   when the recipient already has one or more cached representations,
+   a sender <bcp14>SHOULD NOT</bcp14> generate representation metadata other
+   than the above listed fields unless said metadata exists for the
+   purpose of guiding cache updates (e.g., <x:ref>Last-Modified</x:ref> might
+   be useful if the response does not have an <x:ref>ETag</x:ref> field).
+</t>
+<t>
+   Requirements on a cache that receives a 304 response are defined in
+   <xref target="CACHING" x:rel="#freshening.responses"/>. If the conditional request originated with an
+   outbound client, such as a user agent with its own cache sending a
+   conditional GET to a shared proxy, then the proxy <bcp14>SHOULD</bcp14> forward the
+   304 response to that client.
+</t>
+<t>
+   A 304 response is terminated by the end of the header section;
+   it cannot contain content or trailers.
+</t>
+</section>
+
+<section title="305 Use Proxy" anchor="status.305">
+  <iref primary="true" item="305 Use Proxy (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="305 (Use Proxy)"/>
+<t>
+   The 305 (Use Proxy) status code was defined in a previous
+   version of this specification and is now deprecated (<xref target="RFC7231" x:fmt="of" x:sec="B"/>).
+</t>
+</section>
+
+<section title="306 (Unused)" anchor="status.306">
+  <iref primary="true" item="306 (Unused) (status code)" x:for-anchor=""/>
+<t>
+   The 306 status code was defined in a previous version of this
+   specification, is no longer used, and the code is reserved.
+</t>
+</section>
+
+<section title="307 Temporary Redirect" anchor="status.307">
+  <iref primary="true" item="307 Temporary Redirect (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="307"/>
+  <x:anchor-alias value="307 (Temporary Redirect)"/>
+<t>
+   The 307 (Temporary Redirect) status code indicates that the
+   <x:ref>target resource</x:ref> resides temporarily under a different URI
+   and the user agent <bcp14>MUST NOT</bcp14> change the request method if it performs an
+   automatic redirection to that URI.
+   Since the redirection can change over time, the client ought to continue
+   using the original target URI for future requests.
+</t>
+<t>
+   The server <bcp14>SHOULD</bcp14> generate a <x:ref>Location</x:ref> header field in the
+   response containing a URI reference for the different URI.
+   The user agent <bcp14>MAY</bcp14> use the Location field value for automatic redirection.
+   The server's response content usually contains a short hypertext note with
+   a hyperlink to the different URI(s).
+</t>
+</section>
+
+<section title="308 Permanent Redirect" anchor="status.308">
+  <iref primary="true" item="308 Permanent Redirect (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="308"/>
+  <x:anchor-alias value="308 (Permanent Redirect)"/>
+<t>
+   The 308 (Permanent Redirect) status code indicates that the
+   <x:ref>target resource</x:ref> has been assigned a new permanent URI and
+   any future references to this resource ought to use one of the enclosed
+   URIs. The server is suggesting that a user agent with link-editing capability
+   can permanently replace references to the target URI with one of the
+   new references sent by the server. However, this suggestion is usually
+   ignored unless the user agent is actively editing references
+   (e.g., engaged in authoring content), the connection is secured, and
+   the origin server is a trusted authority for the content being edited.
+</t>
+<t>
+   The server <bcp14>SHOULD</bcp14> generate a <x:ref>Location</x:ref> header field in the
+   response containing a preferred URI reference for the new permanent URI.
+   The user agent <bcp14>MAY</bcp14> use the Location field value for automatic redirection.
+   The server's response content usually contains a short hypertext note with
+   a hyperlink to the new URI(s).
+</t>
+<t>
+   A 308 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> This status code is much younger (June 2014) than its sibling codes and thus
+    might not be recognized everywhere. See <xref target="RFC7538" x:fmt="of" x:sec="4"/>
+    for deployment considerations.
+  </t>
+</aside>
+</section>
+</section>
+
+<section title="Client Error 4xx" anchor="status.4xx">
+  <x:anchor-alias value="4xx"/>
+  <x:anchor-alias value="4xx (Client Error)"/>
+  <iref primary="true" item="4xx Client Error (status code class)" x:for-anchor=""/>
+  <iref primary="true" item="Status Codes Classes" subitem="4xx Client Error" x:for-anchor=""/>
+<t>
+   The 4xx (Client Error) class of status code indicates that
+   the client seems to have erred. Except when responding to a HEAD request,
+   the server <bcp14>SHOULD</bcp14> send a representation containing an explanation of
+   the error situation, and whether it is a temporary or permanent condition.
+   These status codes are applicable to any request method. User agents
+   <bcp14>SHOULD</bcp14> display any included representation to the user.
+</t>
+
+<section title="400 Bad Request" anchor="status.400">
+  <iref primary="true" item="400 Bad Request (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="400 (Bad Request)"/>
+<t>
+   The 400 (Bad Request) status code indicates that the server
+   cannot or will not process the request due to something that is perceived
+   to be a client error (e.g., malformed request syntax, invalid request
+   message framing, or deceptive request routing).
+</t>
+</section>
+
+<section title="401 Unauthorized" anchor="status.401">
+  <iref primary="true" item="401 Unauthorized (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="401 (Unauthorized)"/>
+<t>
+   The 401 (Unauthorized) status code indicates that the
+   request has not been applied because it lacks valid authentication
+   credentials for the target resource.
+   The server generating a 401 response <bcp14>MUST</bcp14> send a
+   <x:ref>WWW-Authenticate</x:ref> header field
+   (<xref target="field.www-authenticate"/>)
+   containing at least one challenge applicable to the target resource.
+</t>
+<t>
+   If the request included authentication credentials, then the 401 response
+   indicates that authorization has been refused for those credentials.
+   The user agent <bcp14>MAY</bcp14> repeat the request with a new or replaced
+   <x:ref>Authorization</x:ref> header field (<xref target="field.authorization"/>).
+   If the 401 response contains the same challenge as the prior response, and
+   the user agent has already attempted authentication at least once, then the
+   user agent <bcp14>SHOULD</bcp14> present the enclosed representation to the user, since
+   it usually contains relevant diagnostic information.
+</t>
+</section>
+
+<section title="402 Payment Required" anchor="status.402">
+  <iref primary="true" item="402 Payment Required (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="402 (Payment Required)"/>
+<t>
+   The 402 (Payment Required) status code is reserved for
+   future use.
+</t>
+</section>
+
+<section title="403 Forbidden" anchor="status.403">
+  <iref primary="true" item="403 Forbidden (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="403 (Forbidden)"/>
+<t>
+   The 403 (Forbidden) status code indicates that the
+   server understood the request but refuses to fulfill it.
+   A server that wishes to make public why the request has been forbidden
+   can describe that reason in the response content (if any).
+</t>
+<t>
+   If authentication credentials were provided in the request, the
+   server considers them insufficient to grant access.
+   The client <bcp14>SHOULD NOT</bcp14> automatically repeat the request with the same
+   credentials.
+   The client <bcp14>MAY</bcp14> repeat the request with new or different credentials.
+   However, a request might be forbidden for reasons unrelated to the
+   credentials.
+</t>
+<t>
+   An origin server that wishes to "hide" the current existence of a forbidden
+   <x:ref>target resource</x:ref> <bcp14>MAY</bcp14> instead respond with a status
+   code of <x:ref>404 (Not Found)</x:ref>.
+</t>
+</section>
+
+<section title="404 Not Found" anchor="status.404">
+  <iref primary="true" item="404 Not Found (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="404 (Not Found)"/>
+<t>
+   The 404 (Not Found) status code indicates that the origin
+   server did not find a current representation for the
+   <x:ref>target resource</x:ref> or is not willing to disclose that one
+   exists. A 404 status code does not indicate whether this lack of representation
+   is temporary or permanent; the <x:ref>410 (Gone)</x:ref> status code is
+   preferred over 404 if the origin server knows, presumably through some
+   configurable means, that the condition is likely to be permanent.
+</t>
+<t>
+   A 404 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+</section>
+
+<section title="405 Method Not Allowed" anchor="status.405">
+  <iref primary="true" item="405 Method Not Allowed (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="405 (Method Not Allowed)"/>
+<t>
+   The 405 (Method Not Allowed) status code indicates that the
+   method received in the request-line is known by the origin server but
+   not supported by the <x:ref>target resource</x:ref>.
+   The origin server <bcp14>MUST</bcp14> generate an <x:ref>Allow</x:ref> header field in
+   a 405 response containing a list of the target resource's currently
+   supported methods.
+</t>
+<t>
+   A 405 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+
+</section>
+
+<section title="406 Not Acceptable" anchor="status.406">
+  <iref primary="true" item="406 Not Acceptable (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="406 (Not Acceptable)"/>
+<t>
+   The 406 (Not Acceptable) status code indicates that the
+   <x:ref>target resource</x:ref> does not have a current representation that
+   would be acceptable to the user agent, according to the
+   <x:ref>proactive negotiation</x:ref> header fields received in the request
+   (<xref target="proactive.negotiation"/>), and the server is unwilling to supply a
+   default representation.
+</t>
+<t>
+   The server <bcp14>SHOULD</bcp14> generate content containing a list of available
+   representation characteristics and corresponding resource identifiers from
+   which the user or user agent can choose the one most appropriate.
+   A user agent <bcp14>MAY</bcp14> automatically select the most appropriate choice from
+   that list. However, this specification does not define any standard for
+   such automatic selection, as described in <xref target="status.300"/>.
+</t>
+</section>
+
+<section title="407 Proxy Authentication Required" anchor="status.407">
+  <iref primary="true" item="407 Proxy Authentication Required (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="407 (Proxy Authentication Required)"/>
+<t>
+   The 407 (Proxy Authentication Required) status code is
+   similar to <x:ref>401 (Unauthorized)</x:ref>, but it indicates that the client
+   needs to authenticate itself in order to use a proxy for this request.
+   The proxy <bcp14>MUST</bcp14> send a <x:ref>Proxy-Authenticate</x:ref> header field
+   (<xref target="field.proxy-authenticate"/>) containing a challenge
+   applicable to that proxy for the request. The client <bcp14>MAY</bcp14> repeat
+   the request with a new or replaced <x:ref>Proxy-Authorization</x:ref>
+   header field (<xref target="field.proxy-authorization"/>).
+</t>
+</section>
+
+<section title="408 Request Timeout" anchor="status.408">
+  <iref primary="true" item="408 Request Timeout (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="408 (Request Timeout)"/>
+<t>
+   The 408 (Request Timeout) status code indicates
+   that the server did not receive a complete request message within the time
+   that it was prepared to wait.
+</t>
+<t>
+   If the client has an outstanding request in transit, it <bcp14>MAY</bcp14> repeat that
+   request. If the current connection is not usable (e.g., as it would be in
+   HTTP/1.1 because request delimitation is lost), a new connection will be
+   used.
+</t>
+</section>
+
+<section title="409 Conflict" anchor="status.409">
+  <iref primary="true" item="409 Conflict (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="409 (Conflict)"/>
+<t>
+   The 409 (Conflict) status code indicates that the request
+   could not be completed due to a conflict with the current state of the target
+   resource. This code is used in situations where the user might be able to
+   resolve the conflict and resubmit the request. The server <bcp14>SHOULD</bcp14> generate
+   content that includes enough information for a user to recognize the
+   source of the conflict.
+</t>
+<t>
+   Conflicts are most likely to occur in response to a PUT request. For
+   example, if versioning were being used and the representation being PUT
+   included changes to a resource that conflict with those made by an
+   earlier (third-party) request, the origin server might use a 409 response
+   to indicate that it can't complete the request. In this case, the response
+   representation would likely contain information useful for merging the
+   differences based on the revision history.
+</t>
+</section>
+
+<section title="410 Gone" anchor="status.410">
+  <iref primary="true" item="410 Gone (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="410 (Gone)"/>
+<t>
+   The 410 (Gone) status code indicates that access to the
+   <x:ref>target resource</x:ref> is no longer available at the origin
+   server and that this condition is likely to be permanent. If the origin
+   server does not know, or has no facility to determine, whether or not the
+   condition is permanent, the status code <x:ref>404 (Not Found)</x:ref>
+   ought to be used instead.
+</t>
+<t>
+   The 410 response is primarily intended to assist the task of web
+   maintenance by notifying the recipient that the resource is
+   intentionally unavailable and that the server owners desire that
+   remote links to that resource be removed. Such an event is common for
+   limited-time, promotional services and for resources belonging to
+   individuals no longer associated with the origin server's site. It is not
+   necessary to mark all permanently unavailable resources as "gone" or
+   to keep the mark for any length of time — that is left to the
+   discretion of the server owner.
+</t>
+<t>
+   A 410 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+</section>
+
+<section title="411 Length Required" anchor="status.411">
+  <iref primary="true" item="411 Length Required (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="411 (Length Required)"/>
+<t>
+   The 411 (Length Required) status code indicates that the
+   server refuses to accept the request without a defined
+   <x:ref>Content-Length</x:ref> (<xref target="field.content-length"/>).
+   The client <bcp14>MAY</bcp14> repeat the request if it adds a valid Content-Length
+   header field containing the length of the request content.
+</t>
+</section>
+
+<section title="412 Precondition Failed" anchor="status.412">
+  <iref primary="true" item="412 Precondition Failed (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="412 (Precondition Failed)"/>
+<t>
+   The 412 (Precondition Failed) status code indicates that one
+   or more conditions given in the request header fields evaluated to false
+   when tested on the server (<xref target="conditional.requests"/>). This
+   response status code allows the client to place preconditions on the
+   current resource state (its current representations and metadata) and,
+   thus, prevent the request method from being applied if the target resource
+   is in an unexpected state.
+</t>
+</section>
+
+<section title="413 Content Too Large" anchor="status.413">
+  <iref primary="true" item="413 Content Too Large (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="413 (Content Too Large)"/>
+<t>
+   The 413 (Content Too Large) status code indicates
+   that the server is refusing to process a request because the request
+   content is larger than the server is willing or able to process.
+   The server <bcp14>MAY</bcp14> terminate the request, if the protocol version in use
+   allows it; otherwise, the server <bcp14>MAY</bcp14> close the connection.
+</t>
+<t>
+   If the condition is temporary, the server <bcp14>SHOULD</bcp14> generate a
+   <x:ref>Retry-After</x:ref> header field to indicate that it is temporary
+   and after what time the client <bcp14>MAY</bcp14> try again.
+</t>
+</section>
+
+<section title="414 URI Too Long" anchor="status.414">
+  <iref primary="true" item="414 URI Too Long (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="414 (URI Too Long)"/>
+<t>
+   The 414 (URI Too Long) status code indicates that the server
+   is refusing to service the request because the
+   target URI is longer than the server is willing to
+   interpret. This rare condition is only likely to occur when a client has
+   improperly converted a POST request to a GET request with long query
+   information, when the client has descended into an infinite loop of
+   redirection (e.g., a redirected URI prefix that points to a suffix of
+   itself) or when the server is under attack by a client attempting to
+   exploit potential security holes.
+</t>
+<t>
+   A 414 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+
+</section>
+
+<section title="415 Unsupported Media Type" anchor="status.415">
+  <iref primary="true" item="415 Unsupported Media Type (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="415 (Unsupported Media Type)"/>
+<t>
+   The 415 (Unsupported Media Type) status code indicates that
+   the origin server is refusing to service the request because the content is
+   in a format not supported by this method on the <x:ref>target resource</x:ref>.
+</t>
+<t>
+   The format problem might be due to the request's indicated
+   <x:ref>Content-Type</x:ref> or <x:ref>Content-Encoding</x:ref>, or as a
+   result of inspecting the data directly.
+</t>
+<t>
+   If the problem was caused by an unsupported content coding, the
+   <x:ref>Accept-Encoding</x:ref> response header field
+   (<xref target="field.accept-encoding"/>) ought to be
+   used to indicate which (if any) content codings would have been accepted
+   in the request.
+</t>
+<t>
+   On the other hand, if the cause was an unsupported media type, the
+   <x:ref>Accept</x:ref> response header field (<xref target="field.accept"/>)
+   can be used to indicate which media types would have been accepted
+   in the request.
+</t>
+</section>
+
+<section title="416 Range Not Satisfiable" anchor="status.416">
+  <iref primary="true" item="416 Range Not Satisfiable (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="416 (Range Not Satisfiable)"/>
+<t>
+   The 416 (Range Not Satisfiable) status code indicates that
+   the set of ranges in the request's <x:ref>Range</x:ref> header field
+   (<xref target="field.range"/>) has been rejected either because none of
+   the requested ranges are satisfiable or because the client has requested
+   an excessive number of small or overlapping ranges (a potential denial of
+   service attack).
+</t>
+<t>
+   Each range unit defines what is required for its own range sets to be
+   satisfiable. For example, <xref target="byte.ranges"/> defines what makes
+   a bytes range set satisfiable.
+</t>
+<t>
+   A server that generates a 416 response to a byte-range request <bcp14>SHOULD</bcp14>
+   generate a <x:ref>Content-Range</x:ref> header field
+   specifying the current length of the selected representation
+   (<xref target="field.content-range"/>).
+</t>
+<t>
+   For example:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 416 Range Not Satisfiable
+Date: Fri, 20 Jan 2012 15:41:54 GMT
+Content-Range: bytes */47022
+</sourcecode>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Because servers are free to ignore <x:ref>Range</x:ref>, many
+    implementations will respond with the entire selected representation
+    in a <x:ref>200 (OK)</x:ref> response. That is partly because
+    most clients are prepared to receive a <x:ref>200 (OK)</x:ref> to
+    complete the task (albeit less efficiently) and partly because clients
+    might not stop making an invalid range request until they have received
+    a complete representation. Thus, clients cannot depend on receiving a
+    <x:ref>416 (Range Not Satisfiable)</x:ref> response even when it is most
+    appropriate.
+  </t>
+</aside>
+</section>
+
+<section title="417 Expectation Failed" anchor="status.417">
+  <iref primary="true" item="417 Expectation Failed (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="417 (Expectation Failed)"/>
+<t>
+   The 417 (Expectation Failed) status code indicates that the
+   expectation given in the request's <x:ref>Expect</x:ref> header field
+   (<xref target="field.expect"/>) could not be met by at least one of the
+   inbound servers.
+</t>
+</section>
+
+<section title="418 (Unused)" anchor="status.418">
+   <iref primary="true" item="418 (Unused) (status code)" x:for-anchor=""/>
+   <x:anchor-alias value="418 (Unused)"/>
+<t>
+   <xref target="RFC2324"/> was an April 1 RFC that lampooned the various ways
+   HTTP was abused; one such abuse was the definition of an
+   application-specific 418 status code, which has been deployed as a joke
+   often enough for the code to be unusable for any future use.
+</t>
+<t>
+   Therefore, the 418 status code is reserved in the IANA HTTP Status Code
+   Registry. This indicates that the status code cannot be assigned to other
+   applications currently. If future circumstances require its use (e.g.,
+   exhaustion of 4NN status codes), it can be re-assigned to another use.
+</t>
+</section>
+
+<section title="421 Misdirected Request" anchor="status.421">
+  <iref primary="true" item="421 Misdirected Request (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="421 (Misdirected Request)"/>
+<t>
+   The 421 (Misdirected Request) status code indicates that the request was
+   directed at a server that is unable or unwilling to produce an
+   authoritative response for the target URI. An origin server (or gateway
+   acting on behalf of the origin server) sends 421 to reject a target URI
+   that does not match an <x:ref>origin</x:ref> for which the server has been
+   configured (<xref target="origin"/>) or does not match the connection
+   context over which the request was received
+   (<xref target="routing.reject"/>).
+</t>
+<t>
+   A client that receives a 421 (Misdirected Request) response <bcp14>MAY</bcp14> retry the
+   request, whether or not the request method is idempotent, over a different
+   connection, such as a fresh connection specific to the target resource's
+   origin, or via an alternative service <xref target="ALTSVC"/>.
+</t>
+<t>
+   A proxy <bcp14>MUST NOT</bcp14> generate a 421 response.
+</t>
+</section>
+
+<section title="422 Unprocessable Content" anchor="status.422">
+  <iref primary="true" item="422 Unprocessable Content (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="422 (Unprocessable Content)"/>
+<t>
+   The 422 (Unprocessable Content) status code indicates that the server
+   understands the content type of the request content (hence a
+   <x:ref>415 (Unsupported Media Type)</x:ref> status code is inappropriate),
+   and the syntax of the request content is correct, but it was unable to process
+   the contained instructions. For example, this status code can be sent if
+   an XML request content contains well-formed (i.e., syntactically correct), but
+   semantically erroneous XML instructions.
+</t>
+</section>
+
+<section title="426 Upgrade Required" anchor="status.426">
+  <iref primary="true" item="426 Upgrade Required (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="426 (Upgrade Required)"/>
+<t>
+   The 426 (Upgrade Required) status code indicates that the
+   server refuses to perform the request using the current protocol but might
+   be willing to do so after the client upgrades to a different protocol.
+   The server <bcp14>MUST</bcp14> send an <x:ref>Upgrade</x:ref> header field in a 426
+   response to indicate the required protocol(s) (<xref target="field.upgrade"/>).
+</t>
+<t>
+   Example:
+</t>
+<sourcecode type="http-message">
+HTTP/1.1 426 Upgrade Required
+Upgrade: HTTP/3.0
+Connection: Upgrade
+Content-Length: <x:length-of target="s426body"/>
+Content-Type: text/plain
+
+<x:span anchor="s426body">This service requires use of the HTTP/3.0 protocol.
+</x:span></sourcecode>
+</section>
+</section>
+
+<section title="Server Error 5xx" anchor="status.5xx">
+  <x:anchor-alias value="5xx"/>
+  <x:anchor-alias value="5xx (Server Error)"/>
+  <iref primary="true" item="5xx Server Error (status code class)" x:for-anchor=""/>
+  <iref primary="true" item="Status Codes Classes" subitem="5xx Server Error" x:for-anchor=""/>
+<t>
+   The 5xx (Server Error) class of status code indicates that
+   the server is aware that it has erred or is incapable of performing the
+   requested method.
+   Except when responding to a HEAD request, the server <bcp14>SHOULD</bcp14> send a
+   representation containing an explanation of the error situation, and
+   whether it is a temporary or permanent condition.
+   A user agent <bcp14>SHOULD</bcp14> display any included representation to the user.
+   These status codes are applicable to any request method.
+</t>
+
+<section title="500 Internal Server Error" anchor="status.500">
+  <iref primary="true" item="500 Internal Server Error (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="500 (Internal Server Error)"/>
+<t>
+   The 500 (Internal Server Error) status code indicates that
+   the server encountered an unexpected condition that prevented it from
+   fulfilling the request.
+</t>
+</section>
+
+<section title="501 Not Implemented" anchor="status.501">
+  <iref primary="true" item="501 Not Implemented (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="501 (Not Implemented)"/>
+<t>
+   The 501 (Not Implemented) status code indicates that the
+   server does not support the functionality required to fulfill the request.
+   This is the appropriate response when the server does not recognize the
+   request method and is not capable of supporting it for any resource.
+</t>
+<t>
+   A 501 response is heuristically cacheable; i.e., unless otherwise indicated by
+   the method definition or explicit cache controls (see <xref target="CACHING" x:rel="#heuristic.freshness"/>).
+</t>
+
+</section>
+
+<section title="502 Bad Gateway" anchor="status.502">
+  <iref primary="true" item="502 Bad Gateway (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="502 (Bad Gateway)"/>
+<t>
+   The 502 (Bad Gateway) status code indicates that the server,
+   while acting as a gateway or proxy, received an invalid response from an
+   inbound server it accessed while attempting to fulfill the request.
+</t>
+</section>
+
+<section title="503 Service Unavailable" anchor="status.503">
+  <iref primary="true" item="503 Service Unavailable (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="503 (Service Unavailable)"/>
+<t>
+   The 503 (Service Unavailable) status code indicates that the
+   server is currently unable to handle the request due to a temporary overload
+   or scheduled maintenance, which will likely be alleviated after some delay.
+   The server <bcp14>MAY</bcp14> send a <x:ref>Retry-After</x:ref> header field
+   (<xref target="field.retry-after"/>) to suggest an appropriate
+   amount of time for the client to wait before retrying the request.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> The existence of the 503 status code does not imply that a
+    server has to use it when becoming overloaded. Some servers might
+    simply refuse the connection.
+  </t>
+</aside>
+</section>
+
+<section title="504 Gateway Timeout" anchor="status.504">
+  <iref primary="true" item="504 Gateway Timeout (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="504 (Gateway Timeout)"/>
+<t>
+   The 504 (Gateway Timeout) status code indicates that the
+   server, while acting as a gateway or proxy, did not receive a timely
+   response from an upstream server it needed to access in order to
+   complete the request.
+</t>
+</section>
+
+<section title="505 HTTP Version Not Supported" anchor="status.505">
+  <iref primary="true" item="505 HTTP Version Not Supported (status code)" x:for-anchor=""/>
+  <x:anchor-alias value="505 (HTTP Version Not Supported)"/>
+<t>
+   The 505 (HTTP Version Not Supported) status code indicates
+   that the server does not support, or refuses to support, the major version
+   of HTTP that was used in the request message. The server is indicating that
+   it is unable or unwilling to complete the request using the same major
+   version as the client, as described in <xref target="protocol.version"/>, other than with this
+   error message. The server <bcp14>SHOULD</bcp14> generate a representation for the 505
+   response that describes why that version is not supported and what other
+   protocols are supported by that server.
+</t>
+</section>
+</section>
+</section>
+
+<section title="Extending HTTP" anchor="extending">
+<t>
+   HTTP defines a number of generic extension points that can be used to
+   introduce capabilities to the protocol without introducing a new version,
+   including methods, status codes, field names, and further extensibility
+   points within defined fields, such as authentication schemes and
+   cache directives (see Cache-Control extensions in <xref target="CACHING" x:rel="#cache.control.extensions"/>). Because the semantics of HTTP are
+   not versioned, these extension points are persistent; the version of the
+   protocol in use does not affect their semantics.
+</t>
+<t>
+   Version-independent extensions are discouraged from depending on or
+   interacting with the specific version of the protocol in use. When this is
+   unavoidable, careful consideration needs to be given to how the extension
+   can interoperate across versions.
+</t>
+<t>
+   Additionally, specific versions of HTTP might have their own extensibility
+   points, such as transfer codings in HTTP/1.1 (<xref target="HTTP11" x:rel="#field.transfer-encoding"/>) and HTTP/2 SETTINGS or frame types
+   (<xref target="HTTP2"/>). These extension points are specific to the
+   version of the protocol they occur within.
+</t>
+<t>
+   Version-specific extensions cannot override or modify the semantics of
+   a version-independent mechanism or extension point (like a method or
+   header field) without explicitly being allowed by that protocol element. For
+   example, the CONNECT method (<xref target="CONNECT"/>) allows this.
+</t>
+<t>
+   These guidelines assure that the protocol operates correctly and
+   predictably, even when parts of the path implement different versions of
+   HTTP.
+</t>
+
+<section title="Method Extensibility" anchor="method.extensibility">
+
+<section title="Method Registry" anchor="method.registry">
+<t>
+  The "Hypertext Transfer Protocol (HTTP) Method Registry", maintained by
+  IANA at <eref target="https://www.iana.org/assignments/http-methods"/>,
+  registers <x:ref>method</x:ref> names.
+</t>
+<t>
+  HTTP method registrations <bcp14>MUST</bcp14> include the following fields:
+</t>
+<ul>
+  <li>Method Name (see <xref target="methods"/>)</li>
+  <li>Safe ("yes" or "no", see <xref target="safe.methods"/>)</li>
+  <li>Idempotent ("yes" or "no", see <xref target="idempotent.methods"/>)</li>
+  <li>Pointer to specification text</li>
+</ul>
+<t>
+  Values to be added to this namespace require IETF Review
+  (see <xref target="RFC8126" x:fmt="," x:sec="4.8"/>).
+</t>
+</section>
+
+<section title="Considerations for New Methods" anchor="considerations.for.new.methods">
+<t>
+   Standardized methods are generic; that is, they are potentially
+   applicable to any resource, not just one particular media type, kind of
+   resource, or application. As such, it is preferred that new methods
+   be registered in a document that isn't specific to a single application or
+   data format, since orthogonal technologies deserve orthogonal specification.
+</t>
+<t>
+   Since message parsing (<xref target="message.abstraction"/>) needs to be
+   independent of method
+   semantics (aside from responses to HEAD), definitions of new methods
+   cannot change the parsing algorithm or prohibit the presence of content
+   on either the request or the response message.
+   Definitions of new methods can specify that only a zero-length content
+   is allowed by requiring a Content-Length header field with a value of "0".
+</t>
+<t>
+   Likewise, new methods cannot use the special host:port and asterisk forms of
+   request target that are allowed for <x:ref>CONNECT</x:ref> and
+   <x:ref>OPTIONS</x:ref>, respectively (<xref target="target.resource"/>).
+   A full URI in absolute form is needed for the target URI, which means either
+   the request target needs to be sent in absolute form or the target URI will
+   be reconstructed from the request context in the same way it is for other
+   methods.
+</t>
+<t>
+   A new method definition needs to indicate whether it is safe (<xref target="safe.methods"/>), idempotent (<xref target="idempotent.methods"/>),
+   cacheable (<xref target="cacheable.methods"/>), what
+   semantics are to be associated with the request content (if any), and what
+   refinements the method makes to header field or status code semantics.
+   If the new method is cacheable, its definition ought to describe how, and
+   under what conditions, a cache can store a response and use it to satisfy a
+   subsequent request.
+   The new method ought to describe whether it can be made conditional
+   (<xref target="preconditions"/>) and, if so, how a server responds
+   when the condition is false.
+   Likewise, if the new method might have some use for partial response
+   semantics (<xref target="field.range"/>), it ought to document this, too.
+</t>
+<aside>
+  <t>
+    <x:h>Note:</x:h> Avoid defining a method name that starts with "M-", since that
+    prefix might be misinterpreted as having the semantics assigned to it
+    by <xref target="RFC2774"/>.
+  </t>
+</aside>
+</section>
+</section>
+
+<section title="Status Code Extensibility" anchor="status.code.extensibility">
+
+<section title="Status Code Registry" anchor="status.code.registry">
+<t>
+   The "Hypertext Transfer Protocol (HTTP) Status Code Registry", maintained
+   by IANA at <eref target="https://www.iana.org/assignments/http-status-codes"/>,
+   registers <x:ref>status code</x:ref> numbers.
+</t>
+<t>
+   A registration <bcp14>MUST</bcp14> include the following fields:
+</t>
+<ul>
+  <li>Status Code (3 digits)</li>
+  <li>Short Description</li>
+  <li>Pointer to specification text</li>
+</ul>
+<t>
+   Values to be added to the HTTP status code namespace require IETF Review
+   (see <xref target="RFC8126" x:fmt="," x:sec="4.8"/>).
+</t>
+</section>
+
+<section title="Considerations for New Status Codes" anchor="considerations.for.new.status.codes">
+<t>
+   When it is necessary to express semantics for a response that are not
+   defined by current status codes, a new status code can be registered.
+   Status codes are generic; they are potentially applicable to any resource,
+   not just one particular media type, kind of resource, or application of
+   HTTP. As such, it is preferred that new status codes be registered in a
+   document that isn't specific to a single application.
+</t>
+<t>
+   New status codes are required to fall under one of the categories
+   defined in <xref target="status.codes"/>. To allow existing parsers to
+   process the response message, new status codes cannot disallow content,
+   although they can mandate a zero-length content.
+</t>
+<t>
+   Proposals for new status codes that are not yet widely deployed ought to
+   avoid allocating a specific number for the code until there is clear
+   consensus that it will be registered; instead, early drafts can use a
+   notation such as "4NN", or "3N0" .. "3N9", to indicate the class
+   of the proposed status code(s) without consuming a number prematurely.
+</t>
+<t>
+   The definition of a new status code ought to explain the request
+   conditions that would cause a response containing that status code (e.g.,
+   combinations of request header fields and/or method(s)) along with any
+   dependencies on response header fields (e.g., what fields are required,
+   what fields can modify the semantics, and what field semantics are
+   further refined when used with the new status code).
+</t>
+<t>
+   By default, a status code applies only to the request corresponding to the
+   response it occurs within. If a status code applies to a larger scope of
+   applicability — for example, all requests to the resource in question or
+   all requests to a server — this must be explicitly specified. When doing
+   so, it should be noted that not all clients can be expected to
+   consistently apply a larger scope because they might not understand the
+   new status code.
+</t>
+<t>
+   The definition of a new final status code ought to specify whether or not it
+   is heuristically cacheable. Note that any response with a final status code
+   can be cached if the response has explicit freshness information. A status
+   code defined as heuristically cacheable is allowed to be cached without
+   explicit freshness information.
+   Likewise, the definition of a status code can place
+   constraints upon cache behavior if the must-understand cache
+   directive is used. See <xref target="CACHING"/> for more information.
+</t>
+<t>
+   Finally, the definition of a new status code ought to indicate whether the
+   content has any implied association with an identified resource (<xref target="identifying.content"/>).
+</t>
+</section>
+</section>
+
+<section title="Field Extensibility" anchor="fields.extensibility">
+  <x:anchor-alias value="header.extensibility"/>
+
+<t>
+  HTTP's most widely used extensibility point is the definition of new header and
+  trailer fields.
+</t>
+<t>
+  New fields can be defined such that, when they are understood by a
+  recipient, they override or enhance the interpretation of previously
+  defined fields, define preconditions on request evaluation, or
+  refine the meaning of responses.
+</t>
+<t>
+   However, defining a field doesn't guarantee its deployment or recognition
+   by recipients. Most fields are designed with the expectation that a recipient
+   can safely ignore (but forward downstream) any field not recognized.
+   In other cases, the sender's ability to understand a given field might be
+   indicated by its prior communication, perhaps in the protocol version
+   or fields that it sent in prior messages, or its use of a specific media type.
+   Likewise, direct inspection of support might be possible through an
+   OPTIONS request or by interacting with a defined well-known URI
+   <xref target="RFC8615"/> if such inspection is defined along with
+   the field being introduced.
+</t>
+
+<section title="Field Name Registry" anchor="fields.registry">
+  <x:anchor-alias value="header.name.registry"/>
+<t>
+   The "Hypertext Transfer Protocol (HTTP) Field Name Registry" defines the
+   namespace for HTTP field names.
+</t>
+<t>
+   Any party can request registration of an HTTP field. See <xref target="considerations.for.new.fields"/> for considerations to take
+   into account when creating a new HTTP field.
+</t>
+<t>
+   The "Hypertext Transfer Protocol (HTTP) Field Name Registry" is located at
+   <eref target="https://www.iana.org/assignments/http-fields/"/>.
+   Registration requests can be made by following the instructions located
+   there or by sending an email to the "ietf-http-wg@w3.org" mailing list.
+</t>
+<t>
+   Field names are registered on the advice of a designated expert
+   (appointed by the IESG or their delegate). Fields with the status
+   'permanent' are Specification Required
+   (<xref target="RFC8126" x:sec="4.6" x:fmt=","/>).
+</t>
+<t>
+   Registration requests consist of the following information:
+</t>
+<dl newline="true">
+  <dt>Field name:</dt>
+  <dd>
+    The requested field name. It <bcp14>MUST</bcp14> conform to the
+    field-name syntax defined in <xref target="fields.names"/>, and it <bcp14>SHOULD</bcp14> be
+    restricted to just letters, digits, and hyphen ('-')
+    characters, with the first character being a letter.
+  </dd>
+  <dt>Status:</dt>
+  <dd>
+    "permanent", "provisional", "deprecated", or "obsoleted".
+  </dd>
+  <dt>Specification document(s):</dt>
+  <dd>
+    Reference to the document that specifies
+    the field, preferably including a URI that can be used to retrieve
+    a copy of the document. Optional but encouraged for provisional registrations.
+    An indication of the relevant section(s) can also be included, but is not required.
+  </dd>
+</dl>
+<t>
+   And optionally:
+</t>
+<dl>
+  <dt>Comments:</dt>
+  <dd>
+    Additional information, such as about reserved entries.
+  </dd>
+</dl>
+<t>
+   The expert(s) can define additional fields to be collected in the
+   registry, in consultation with the community.
+</t>
+<t>
+   Standards-defined names have a status of "permanent". Other names can also
+   be registered as permanent if the expert(s) finds that they are in use, in
+   consultation with the community. Other names should be registered as
+   "provisional".
+</t>
+<t>
+   Provisional entries can be removed by the expert(s) if — in consultation
+   with the community — the expert(s) find that they are not in use. The
+   expert(s) can change a provisional entry's status to permanent at any time.
+</t>
+<t>
+   Note that names can be registered by third parties (including the
+   expert(s)) if the expert(s) determines that an unregistered name is widely
+   deployed and not likely to be registered in a timely manner otherwise.
+</t>
+</section>
+
+<section title="Considerations for New Fields" anchor="considerations.for.new.fields">
+<t>
+   HTTP header and trailer fields are a widely used extension point for the protocol.
+   While they can be used in an ad hoc fashion, fields that are intended for
+   wider use need to be carefully documented to ensure interoperability.
+</t>
+<t>
+   In particular, authors of specifications defining new fields are advised to consider
+   and, where appropriate, document the following aspects:
+</t>
+<ul>
+   <li>Under what conditions the field can be used; e.g., only in
+     responses or requests, in all messages, only on responses to a
+     particular request method, etc.</li>
+   <li>Whether the field semantics are further refined by their context,
+     such as their use with certain request methods or status codes.</li>
+   <li>The scope of applicability for the information conveyed.
+     By default, fields apply only to the message they are
+     associated with, but some response fields are designed to apply to all
+     representations of a resource, the resource itself, or an even broader
+     scope. Specifications that expand the scope of a response field will
+     need to carefully consider issues such as content negotiation, the time
+     period of applicability, and (in some cases) multi-tenant server
+     deployments.</li>
+   <li>Under what conditions intermediaries are allowed to insert,
+     delete, or modify the field's value.</li>
+   <li>If the field is allowable in trailers; by
+     default, it will not be (see <xref target="trailers.limitations"/>).</li>
+   <li>Whether it is appropriate or even required to list the field name in the
+     <x:ref>Connection</x:ref> header field (i.e., if the field is to
+     be hop-by-hop; see <xref target="field.connection"/>).</li>
+   <li>Whether the field introduces any additional security considerations, such
+     as disclosure of privacy-related data.</li>
+</ul>
+<t>
+   Request header fields have additional considerations that need to be documented
+   if the default behavior is not appropriate:
+</t>
+<ul>
+   <li>If it is appropriate to list the field name in a
+     <x:ref>Vary</x:ref> response header field (e.g., when the request header
+     field is used by an origin server's content selection algorithm; see
+     <xref target="field.vary"/>).</li>
+   <li>If the field is intended to be stored when received in a PUT
+   request (see <xref target="PUT"/>).</li>
+   <li>If the field ought to be removed when automatically redirecting a
+   request due to security concerns (see <xref target="status.3xx"/>).</li>
+</ul>
+
+
+<section title="Considerations for New Field Names" anchor="considerations.for.new.field.names">
+<t>
+   Authors of specifications defining new fields are advised to choose a short
+   but descriptive field name. Short names avoid needless data transmission;
+   descriptive names avoid confusion and "squatting" on names that might have
+   broader uses.
+</t>
+<t>
+   To that end, limited-use fields (such as a header confined to a single
+   application or use case) are encouraged to use a name that includes that use
+   (or an abbreviation) as a prefix; for example, if the Foo Application needs
+   a Description field, it might use "Foo-Desc"; "Description" is too generic,
+   and "Foo-Description" is needlessly long.
+</t>
+<t>
+   While the field-name syntax is defined to allow any token character, in
+   practice some implementations place limits on the characters they accept
+   in field-names. To be interoperable, new field names <bcp14>SHOULD</bcp14> constrain
+   themselves to alphanumeric characters, "-", and ".", and <bcp14>SHOULD</bcp14>
+   begin with a letter. For example, the underscore
+   ("_") character can be problematic when passed through non-HTTP
+   gateway interfaces (see <xref target="underscore.in.fields"/>).
+</t>
+<t>
+   Field names ought not be prefixed with "X-"; see
+   <xref target="BCP178"/> for further information.
+</t>
+<t>
+   Other prefixes are sometimes used in HTTP field names; for example,
+   "Accept-" is used in many content negotiation headers, and "Content-" is used
+   as explained in <xref target="content"/>. These prefixes are
+   only an aid to recognizing the purpose of a field and do not
+   trigger automatic processing.
+</t>
+</section>
+
+<section title="Considerations for New Field Values" anchor="considerations.for.new.field.values">
+<t>
+   A major task in the definition of a new HTTP field is the specification of
+   the field value syntax: what senders should generate, and how recipients
+   should infer semantics from what is received.
+</t>
+<t>
+   Authors are encouraged (but not required) to use either the ABNF rules in
+   this specification or those in <xref target="RFC8941"/> to define the syntax
+   of new field values.
+</t>
+<t>
+   Authors are advised to carefully consider how the combination of multiple
+   field lines will impact them (see <xref target="fields.order"/>). Because
+   senders might erroneously send multiple values, and both intermediaries
+   and HTTP libraries can perform combination automatically, this applies to
+   all field values — even when only a single value is anticipated.
+</t>
+<t>
+   Therefore, authors are advised to delimit or encode values that contain
+   commas (e.g., with the <x:ref>quoted-string</x:ref> rule of
+   <xref target="quoted.strings"/>, the String data type of
+   <xref target="RFC8941"/>, or a field-specific encoding).
+   This ensures that commas within field data are not confused
+   with the commas that delimit a list value.
+</t>
+<t>
+   For example, the <x:ref>Content-Type</x:ref> field value only allows commas
+   inside quoted strings, which can be reliably parsed even when multiple
+   values are present. The <x:ref>Location</x:ref> field value provides a
+   counter-example that should not be emulated: because URIs can include
+   commas, it is not possible to reliably distinguish between a single value
+   that includes a comma from two values.
+</t>
+<t>
+   Authors of fields with a singleton value (see <xref target="fields.values"/>) are additionally advised to document how to treat
+   messages where the multiple members are present (a sensible default would
+   be to ignore the field, but this might not always be the right choice).
+</t>
+</section>
+</section>
+</section>
+
+<section title="Authentication Scheme Extensibility" anchor="auth.scheme.extensibility">
+
+<section title="Authentication Scheme Registry" anchor="auth.scheme.registry">
+<t>
+   The "Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry"
+   defines the namespace for the authentication schemes in challenges and
+   credentials. It is maintained
+   at <eref target="https://www.iana.org/assignments/http-authschemes"/>.
+</t>
+<t>
+  Registrations <bcp14>MUST</bcp14> include the following fields:
+</t>
+<ul>
+  <li>Authentication Scheme Name</li>
+  <li>Pointer to specification text</li>
+  <li>Notes (optional)</li>
+</ul>
+<t>
+  Values to be added to this namespace require IETF Review
+  (see <xref target="RFC8126" x:fmt="," x:sec="4.8"/>).
+</t>
+</section>
+
+<section title="Considerations for New Authentication Schemes" anchor="considerations.for.new.auth.schemes">
+<t>
+  There are certain aspects of the HTTP Authentication framework that put
+  constraints on how new authentication schemes can work:
+</t>
+<ul>
+  <li><t>
+    HTTP authentication is presumed to be stateless: all of the information
+    necessary to authenticate a request <bcp14>MUST</bcp14> be provided in the request,
+    rather than be dependent on the server remembering prior requests.
+    Authentication based on, or bound to, the underlying connection is
+    outside the scope of this specification and inherently flawed unless
+    steps are taken to ensure that the connection cannot be used by any
+    party other than the authenticated user
+    (see <xref target="connections"/>).
+  </t>
+  </li>
+  <li>
+    <t>
+      The authentication parameter "realm" is reserved for defining protection
+      spaces as described in <xref target="protection.space"/>. New schemes
+      <bcp14>MUST NOT</bcp14> use it in a way incompatible with that definition.
+    </t>
+  </li>
+  <li>
+    <t>
+      The "token68" notation was introduced for compatibility with existing
+      authentication schemes and can only be used once per challenge or credential.
+      Thus, new schemes ought to use the auth-param syntax instead, because
+      otherwise future extensions will be impossible.
+    </t>
+  </li>
+  <li>
+    <t>
+      The parsing of challenges and credentials is defined by this specification
+      and cannot be modified by new authentication schemes. When the auth-param
+      syntax is used, all parameters ought to support both token and
+      quoted-string syntax, and syntactical constraints ought to be defined on
+      the field value after parsing (i.e., quoted-string processing). This is
+      necessary so that recipients can use a generic parser that applies to
+      all authentication schemes.
+    </t>
+    <t>
+      <x:h>Note:</x:h> The fact that the value syntax for the "realm" parameter
+      is restricted to quoted-string was a bad design choice not to be repeated
+      for new parameters.
+    </t>
+  </li>
+  <li>
+    <t>
+      Definitions of new schemes ought to define the treatment of unknown
+      extension parameters. In general, a "must-ignore" rule is preferable
+      to a "must-understand" rule, because otherwise it will be hard to introduce
+      new parameters in the presence of legacy recipients. Furthermore,
+      it's good to describe the policy for defining new parameters (such
+      as "update the specification" or "use this registry").
+    </t>
+  </li>
+  <li>
+    <t>
+      Authentication schemes need to document whether they are usable in
+      origin-server authentication (i.e., using <x:ref>WWW-Authenticate</x:ref>),
+      and/or proxy authentication (i.e., using <x:ref>Proxy-Authenticate</x:ref>).
+    </t>
+  </li>
+  <li>
+    <t>
+      The credentials carried in an <x:ref>Authorization</x:ref> header field are specific to
+      the user agent and, therefore, have the same effect on HTTP caches as the
+      "private" cache response directive (<xref target="CACHING" x:rel="#cache-response-directive.private"/>),
+      within the scope of the request in which they appear.
+    </t>
+    <t>
+      Therefore, new authentication schemes that choose not to carry
+      credentials in the <x:ref>Authorization</x:ref> header field (e.g., using a newly defined
+      header field) will need to explicitly disallow caching, by mandating the use of
+      cache response directives (e.g., "private").
+    </t>
+  </li>
+  <li>
+    <t>
+      Schemes using <x:ref>Authentication-Info</x:ref>, <x:ref>Proxy-Authentication-Info</x:ref>,
+      or any other authentication related response header field need to
+      consider and document the related security considerations (see
+      <xref target="security.auth.add.resp"/>).
+    </t>
+  </li>
+</ul>
+</section>
+</section>
+
+<section title="Range Unit Extensibility" anchor="range.unit.extensibility">
+
+<section title="Range Unit Registry" anchor="range.unit.registry">
+<t>
+   The "HTTP Range Unit Registry" defines the namespace for the range
+   unit names and refers to their corresponding specifications.
+   It is maintained at
+   <eref target="https://www.iana.org/assignments/http-parameters"/>.
+</t>
+<t>
+   Registration of an HTTP Range Unit <bcp14>MUST</bcp14> include the following fields:
+</t>
+<ul>
+  <li>Name</li>
+  <li>Description</li>
+  <li>Pointer to specification text</li>
+</ul>
+<t>
+  Values to be added to this namespace require IETF Review
+  (see <xref target="RFC8126" x:fmt="," x:sec="4.8"/>).
+</t>
+</section>
+
+<section title="Considerations for New Range Units" anchor="considerations.for.new.range.units">
+  <x:anchor-alias value="other-range-unit"/>
+<t>
+   Other range units, such as format-specific boundaries like pages,
+   sections, records, rows, or time, are potentially usable in HTTP for
+   application-specific purposes, but are not commonly used in practice.
+   Implementors of alternative range units ought to consider how they would
+   work with content codings and general-purpose intermediaries.
+</t>
+</section>
+</section>
+
+<section title="Content Coding Extensibility" anchor="content.coding.extensibility">
+
+<section title="Content Coding Registry" anchor="content.coding.registry">
+<t>
+   The "HTTP Content Coding Registry", maintained by
+   IANA at <eref target="https://www.iana.org/assignments/http-parameters/"/>,
+   registers <x:ref>content-coding</x:ref> names.
+</t>
+<t>
+   Content coding registrations <bcp14>MUST</bcp14> include the following fields:
+</t>
+<ul>
+  <li>Name</li>
+  <li>Description</li>
+  <li>Pointer to specification text</li>
+</ul>
+<t>
+   Names of content codings <bcp14>MUST NOT</bcp14> overlap with names of transfer codings
+   (per the "HTTP Transfer Coding Registry" located at
+   <eref target="https://www.iana.org/assignments/http-parameters/"/>) unless
+   the encoding transformation is
+   identical (as is the case for the compression codings defined in
+   <xref target="content.codings"/>).
+</t>
+<t>
+   Values to be added to this namespace require IETF Review
+   (see <xref target="RFC8126" x:fmt="of" x:sec="4.8"/>) and <bcp14>MUST</bcp14>
+   conform to the purpose of content coding defined in
+   <xref target="content.codings"/>.
+</t>
+</section>
+
+<section title="Considerations for New Content Codings" anchor="considerations.for.new.content.codings">
+<t>
+   New content codings ought to be self-descriptive whenever possible, with
+   optional parameters discoverable within the coding format itself, rather
+   than rely on external metadata that might be lost during transit.
+</t>
+</section>
+</section>
+
+<section title="Upgrade Token Registry" anchor="upgrade.token.registry">
+<t>
+   The "Hypertext Transfer Protocol (HTTP) Upgrade Token Registry" defines
+   the namespace for protocol-name tokens used to identify protocols in the
+   <x:ref>Upgrade</x:ref> header field. The registry is maintained at
+   <eref target="https://www.iana.org/assignments/http-upgrade-tokens"/>.
+</t>
+<t>
+   Each registered protocol name is associated with contact information
+   and an optional set of specifications that details how the connection
+   will be processed after it has been upgraded.
+</t>
+<t>
+   Registrations happen on a "First Come First Served" basis (see
+   <xref target="RFC8126" x:sec="4.4" x:fmt="of"/>) and are subject to the
+   following rules:
+</t>
+<ol>
+   <li>A protocol-name token, once registered, stays registered forever.</li>
+   <li>A protocol-name token is case-insensitive and registered with the
+       preferred case to be generated by senders.</li>
+   <li>The registration <bcp14>MUST</bcp14> name a responsible party for the
+       registration.</li>
+   <li>The registration <bcp14>MUST</bcp14> name a point of contact.</li>
+   <li>The registration <bcp14>MAY</bcp14> name a set of specifications associated with
+       that token. Such specifications need not be publicly available.</li>
+   <li>The registration <bcp14>SHOULD</bcp14> name a set of expected "protocol-version"
+       tokens associated with that token at the time of registration.</li>
+   <li>The responsible party <bcp14>MAY</bcp14> change the registration at any time.
+       The IANA will keep a record of all such changes, and make them
+       available upon request.</li>
+   <li>The IESG <bcp14>MAY</bcp14> reassign responsibility for a protocol token.
+       This will normally only be used in the case when a
+       responsible party cannot be contacted.</li>
+</ol>
+</section>
+</section>
+
+<section title="Security Considerations" anchor="security.considerations">
+<t>
+   This section is meant to inform developers, information providers, and
+   users of known security concerns relevant to HTTP semantics and its
+   use for transferring information over the Internet. Considerations related
+   to caching are discussed in <xref target="CACHING" x:rel="#security.considerations"/>,
+   and considerations related to HTTP/1.1 message syntax and parsing are
+   discussed in <xref target="HTTP11" x:rel="#security.considerations"/>.
+</t>
+<t>
+   The list of considerations below is not exhaustive. Most security concerns
+   related to HTTP semantics are about securing server-side applications (code
+   behind the HTTP interface), securing user agent processing of content
+   received via HTTP, or secure use of the Internet in general, rather than
+   security of the protocol. The security considerations for URIs, which
+   are fundamental to HTTP operation, are discussed in
+   <xref target="URI" x:fmt="of" x:sec="7"/>. Various organizations maintain
+   topical information and links to current research on Web application
+   security (e.g., <xref target="OWASP"/>).
+</t>
+
+<section title="Establishing Authority" anchor="establishing.authority">
+  <iref item="authoritative response" primary="true"/>
+  <iref item="phishing" primary="true"/>
+<t>
+   HTTP relies on the notion of an <x:dfn>authoritative response</x:dfn>: a
+   response that has been determined by (or at the direction of) the origin
+   server identified within the target URI to be the most appropriate response
+   for that request given the state of the target resource at the time of
+   response message origination.
+</t>
+<t>
+   When a registered name is used in the authority component, the "http" URI
+   scheme (<xref target="http.uri"/>) relies on the user's local name
+   resolution service to determine where it can find authoritative responses.
+   This means that any attack on a user's network host table, cached names,
+   or name resolution libraries becomes an avenue for attack on establishing
+   authority for "http" URIs. Likewise, the user's choice of server for
+   Domain Name Service (DNS), and the hierarchy of servers from which it
+   obtains resolution results, could impact the authenticity of address
+   mappings; DNS Security Extensions (DNSSEC, <xref target="RFC4033"/>) are
+   one way to improve authenticity, as are the various mechanisms for making
+   DNS requests over more secure transfer protocols.
+</t>
+<t>
+   Furthermore, after an IP address is obtained, establishing authority for
+   an "http" URI is vulnerable to attacks on Internet Protocol routing.
+</t>
+<t>
+   The "https" scheme (<xref target="https.uri"/>) is intended to prevent
+   (or at least reveal) many of these potential attacks on establishing
+   authority, provided that the negotiated connection is secured and
+   the client properly verifies that the communicating server's identity
+   matches the target URI's authority component
+   (<xref target="https.verify"/>). Correctly implementing such verification
+   can be difficult (see <xref target="Georgiev"/>).
+</t>
+<t>
+   Authority for a given origin server can be delegated through protocol
+   extensions; for example, <xref target="ALTSVC"/>. Likewise, the set of
+   servers for which a connection is considered authoritative can be changed
+   with a protocol extension like <xref target="RFC8336"/>.
+</t>
+<t>
+   Providing a response from a non-authoritative source, such as a shared
+   proxy cache, is often useful to improve performance and availability, but
+   only to the extent that the source can be trusted or the distrusted
+   response can be safely used.
+</t>
+<t>
+   Unfortunately, communicating authority to users can be difficult.
+   For example, <x:dfn>phishing</x:dfn> is an attack on the user's perception
+   of authority, where that perception can be misled by presenting similar
+   branding in hypertext, possibly aided by userinfo obfuscating the authority
+   component (see <xref target="http.uri"/>).
+   User agents can reduce the impact of phishing attacks by enabling users to
+   easily inspect a target URI prior to making an action, by prominently
+   distinguishing (or rejecting) userinfo when present, and by not sending
+   stored credentials and cookies when the referring document is from an
+   unknown or untrusted source.
+</t>
+</section>
+
+<section title="Risks of Intermediaries" anchor="risks.intermediaries">
+<t>
+   HTTP intermediaries are inherently situated for on-path attacks.
+   Compromise of
+   the systems on which the intermediaries run can result in serious security
+   and privacy problems. Intermediaries might have access to security-related
+   information, personal information about individual users and
+   organizations, and proprietary information belonging to users and
+   content providers. A compromised intermediary, or an intermediary
+   implemented or configured without regard to security and privacy
+   considerations, might be used in the commission of a wide range of
+   potential attacks.
+</t>
+<t>
+   Intermediaries that contain a shared cache are especially vulnerable
+   to cache poisoning attacks, as described in <xref target="CACHING" x:rel="#security.considerations"/>.
+</t>
+<t>
+   Implementers need to consider the privacy and security
+   implications of their design and coding decisions, and of the
+   configuration options they provide to operators (especially the
+   default configuration).
+</t>
+<t>
+   Intermediaries are no more trustworthy than the people and policies
+   under which they operate; HTTP cannot solve this problem.
+</t>
+</section>
+
+<section title="Attacks Based on File and Path Names" anchor="attack.pathname">
+<t>
+   Origin servers frequently make use of their local file system to manage the
+   mapping from target URI to resource representations.
+   Most file systems are not designed to protect against malicious file
+   or path names. Therefore, an origin server needs to avoid accessing
+   names that have a special significance to the system when mapping the
+   target resource to files, folders, or directories.
+</t>
+<t>
+   For example, UNIX, Microsoft Windows, and other operating systems use ".."
+   as a path component to indicate a directory level above the current one,
+   and they use specially named paths or file names to send data to system devices.
+   Similar naming conventions might exist within other types of storage
+   systems. Likewise, local storage systems have an annoying tendency to
+   prefer user-friendliness over security when handling invalid or unexpected
+   characters, recomposition of decomposed characters, and case-normalization
+   of case-insensitive names.
+</t>
+<t>
+   Attacks based on such special names tend to focus on either denial-of-service
+   (e.g., telling the server to read from a COM port) or disclosure
+   of configuration and source files that are not meant to be served.
+</t>
+</section>
+
+<section title="Attacks Based on Command, Code, or Query Injection" anchor="attack.injection">
+<t>
+   Origin servers often use parameters within the URI as a
+   means of identifying system services, selecting database entries, or
+   choosing a data source. However, data received in a request cannot be
+   trusted. An attacker could construct any of the request data elements
+   (method, target URI, header fields, or content) to contain data that might
+   be misinterpreted as a command, code, or query when passed through a
+   command invocation, language interpreter, or database interface.
+</t>
+<t>
+   For example, SQL injection is a common attack wherein additional query
+   language is inserted within some part of the target URI or header
+   fields (e.g., <x:ref>Host</x:ref>, <x:ref>Referer</x:ref>, etc.).
+   If the received data is used directly within a SELECT statement, the
+   query language might be interpreted as a database command instead of a
+   simple string value. This type of implementation vulnerability is extremely
+   common, in spite of being easy to prevent.
+</t>
+<t>
+   In general, resource implementations ought to avoid use of request data
+   in contexts that are processed or interpreted as instructions.  Parameters
+   ought to be compared to fixed strings and acted upon as a result of that
+   comparison, rather than passed through an interface that is not prepared
+   for untrusted data. Received data that isn't based on fixed parameters
+   ought to be carefully filtered or encoded to avoid being misinterpreted.
+</t>
+<t>
+   Similar considerations apply to request data when it is stored and later
+   processed, such as within log files, monitoring tools, or when included
+   within a data format that allows embedded scripts.
+</t>
+</section>
+
+<section title="Attacks via Protocol Element Length" anchor="attack.protocol.element.length">
+<t>
+   Because HTTP uses mostly textual, character-delimited fields, parsers are
+   often vulnerable to attacks based on sending very long (or very slow)
+   streams of data, particularly where an implementation is expecting a
+   protocol element with no predefined length
+   (<xref target="length.requirements"/>).
+</t>
+<t>
+   To promote interoperability, specific recommendations are made for minimum
+   size limits on fields (<xref target="fields.limits"/>). These are
+   minimum recommendations, chosen to be supportable even by implementations
+   with limited resources; it is expected that most implementations will
+   choose substantially higher limits.
+</t>
+<t>
+   A server can reject a message that
+   has a target URI that is too long (<xref target="status.414"/>) or request content
+   that is too large (<xref target="status.413"/>). Additional status codes related to
+   capacity limits have been defined by extensions to HTTP
+   <xref target="RFC6585"/>.
+</t>
+<t>
+   Recipients ought to carefully limit the extent to which they process other
+   protocol elements, including (but not limited to) request methods, response
+   status phrases, field names, numeric values, and chunk lengths.
+   Failure to limit such processing can result in arbitrary code execution due to
+   buffer or arithmetic
+   overflows, and increased vulnerability to denial-of-service attacks.
+</t>
+</section>
+
+<section title="Attacks Using Shared-Dictionary Compression" anchor="compression.attacks">
+<t>
+   Some attacks on encrypted protocols use the differences in size created by
+   dynamic compression to reveal confidential information; for example, <xref target="BREACH"/>. These attacks rely on creating a redundancy between
+   attacker-controlled content and the confidential information, such that a
+   dynamic compression algorithm using the same dictionary for both content
+   will compress more efficiently when the attacker-controlled content matches
+   parts of the confidential content.
+</t>
+<t>
+   HTTP messages can be compressed in a number of ways, including using TLS
+   compression, content codings, transfer codings, and other extension or
+   version-specific mechanisms.
+</t>
+<t>
+   The most effective mitigation for this risk is to disable compression on
+   sensitive data, or to strictly separate sensitive data from attacker-controlled
+   data so that they cannot share the same compression dictionary. With
+   careful design, a compression scheme can be designed in a way that is not
+   considered exploitable in limited use cases, such as HPACK (<xref target="HPACK"/>).
+</t>
+</section>
+
+<section title="Disclosure of Personal Information" anchor="personal.information">
+<t>
+   Clients are often privy to large amounts of personal information,
+   including both information provided by the user to interact with resources
+   (e.g., the user's name, location, mail address, passwords, encryption
+   keys, etc.) and information about the user's browsing activity over
+   time (e.g., history, bookmarks, etc.). Implementations need to
+   prevent unintentional disclosure of personal information.
+</t>
+</section>
+
+<section title="Privacy of Server Log Information" anchor="privacy.of.server.log.information">
+<t>
+   A server is in the position to save personal data about a user's requests
+   over time, which might identify their reading patterns or subjects of
+   interest.  In particular, log information gathered at an intermediary
+   often contains a history of user agent interaction, across a multitude
+   of sites, that can be traced to individual users.
+</t>
+<t>
+   HTTP log information is confidential in nature; its handling is often
+   constrained by laws and regulations.  Log information needs to be securely
+   stored and appropriate guidelines followed for its analysis.
+   Anonymization of personal information within individual entries helps,
+   but it is generally not sufficient to prevent real log traces from being
+   re-identified based on correlation with other access characteristics.
+   As such, access traces that are keyed to a specific client are unsafe to
+   publish even if the key is pseudonymous.
+</t>
+<t>
+   To minimize the risk of theft or accidental publication, log information
+   ought to be purged of personally identifiable information, including
+   user identifiers, IP addresses, and user-provided query parameters,
+   as soon as that information is no longer necessary to support operational
+   needs for security, auditing, or fraud control.
+</t>
+</section>
+
+<section title="Disclosure of Sensitive Information in URIs" anchor="sensitive.information.in.uris">
+<t>
+   URIs are intended to be shared, not secured, even when they identify secure
+   resources. URIs are often shown on displays, added to templates when a page
+   is printed, and stored in a variety of unprotected bookmark lists.
+   Many servers, proxies, and user agents log or display the target URI
+   in places where it might be visible to third parties.
+   It is therefore unwise to include information within a URI that
+   is sensitive, personally identifiable, or a risk to disclose.
+</t>
+<t>
+   When an application uses client-side mechanisms to construct a target URI
+   out of user-provided information, such as the query fields of a form using
+   GET, potentially sensitive data might be provided that would not be
+   appropriate for disclosure within a URI. POST is often preferred in such
+   cases because it usually doesn't construct a URI; instead, POST of a form
+   transmits the potentially sensitive data in the request content. However, this
+   hinders caching and uses an unsafe method for what would otherwise be a safe
+   request. Alternative workarounds include transforming the user-provided data
+   prior to constructing the URI or filtering the data to only include common
+   values that are not sensitive. Likewise, redirecting the result of a query
+   to a different (server-generated) URI can remove potentially sensitive data
+   from later links and provide a cacheable response for later reuse.
+</t>
+<t>
+   Since the <x:ref>Referer</x:ref> header field tells a target site about the
+   context that resulted in a request, it has the potential to reveal
+   information about the user's immediate browsing history and any personal
+   information that might be found in the referring resource's URI.
+   Limitations on the Referer header field are described in <xref target="field.referer"/> to
+   address some of its security considerations.
+</t>
+</section>
+
+<section title="Application Handling of Field Names" anchor="underscore.in.fields">
+<t>
+   Servers often use non-HTTP gateway interfaces and frameworks to process a received
+   request and produce content for the response. For historical reasons, such interfaces
+   often pass received field names as external variable names, using a name mapping
+   suitable for environment variables.
+</t>
+<t>
+   For example, the Common Gateway Interface (CGI) mapping of protocol-specific
+   meta-variables, defined by <xref target="RFC3875" x:fmt="of" x:sec="4.1.18"/>,
+   is applied to received header fields that do not correspond to one of CGI's
+   standard variables; the mapping consists of prepending "HTTP_" to each name
+   and changing all instances of hyphen ("-") to underscore ("_"). This same mapping
+   has been inherited by many other application frameworks in order to simplify
+   moving applications from one platform to the next.
+</t>
+<t>
+   In CGI, a received <x:ref>Content-Length</x:ref> field would be passed
+   as the meta-variable "CONTENT_LENGTH" with a string value matching the
+   received field's value. In contrast, a received "Content_Length" header field would
+   be passed as the protocol-specific meta-variable "HTTP_CONTENT_LENGTH",
+   which might lead to some confusion if an application mistakenly reads the
+   protocol-specific meta-variable instead of the default one. (This historical practice
+   is why <xref target="considerations.for.new.field.names"/> discourages the creation
+   of new field names that contain an underscore.)
+</t>
+<t>
+   Unfortunately, mapping field names to different interface names can lead to
+   security vulnerabilities if the mapping is incomplete or ambiguous. For example,
+   if an attacker were to send a field named "Transfer_Encoding", a naive interface
+   might map that to the same variable name as the "Transfer-Encoding" field, resulting
+   in a potential request smuggling vulnerability (<xref target="HTTP11" x:rel="#request.smuggling"/>).
+</t>
+<t>
+   To mitigate the associated risks, implementations that perform such
+   mappings are advised to make the mapping unambiguous and complete
+   for the full range of potential octets received as a name (including those
+   that are discouraged or forbidden by the HTTP grammar).
+   For example, a field with an unusual name character might
+   result in the request being blocked, the specific field being removed,
+   or the name being passed with a different prefix to distinguish it from
+   other fields.
+</t>
+</section>
+
+<section title="Disclosure of Fragment after Redirects" anchor="fragment.disclosure">
+<t>
+   Although fragment identifiers used within URI references are not sent
+   in requests, implementers ought to be aware that they will be visible to
+   the user agent and any extensions or scripts running as a result of the
+   response. In particular, when a redirect occurs and the original request's
+   fragment identifier is inherited by the new reference in
+   <x:ref>Location</x:ref> (<xref target="field.location"/>), this might
+   have the effect of disclosing one site's fragment to another site.
+   If the first site uses personal information in fragments, it ought to
+   ensure that redirects to other sites include a (possibly empty) fragment
+   component in order to block that inheritance.
+</t>
+</section>
+
+<section title="Disclosure of Product Information" anchor="disclosure.product.information">
+<t>
+   The <x:ref>User-Agent</x:ref> (<xref target="field.user-agent"/>),
+   <x:ref>Via</x:ref> (<xref target="field.via"/>), and
+   <x:ref>Server</x:ref> (<xref target="field.server"/>) header fields often
+   reveal information about the respective sender's software systems.
+   In theory, this can make it easier for an attacker to exploit known
+   security holes; in practice, attackers tend to try all potential holes
+   regardless of the apparent software versions being used.
+</t>
+<t>
+   Proxies that serve as a portal through a network firewall ought to take
+   special precautions regarding the transfer of header information that might
+   identify hosts behind the firewall. The <x:ref>Via</x:ref> header field
+   allows intermediaries to replace sensitive machine names with pseudonyms.
+</t>
+</section>
+
+<section title="Browser Fingerprinting" anchor="fingerprinting">
+<t>
+   Browser fingerprinting is a set of techniques for identifying a specific
+   user agent over time through its unique set of characteristics. These
+   characteristics might include information related to how it uses the underlying
+   transport protocol,
+   feature capabilities, and scripting environment, though of particular
+   interest here is the set of unique characteristics that might be
+   communicated via HTTP. Fingerprinting is considered a privacy concern
+   because it enables tracking of a user agent's behavior over time
+   (<xref target="Bujlow"/>) without
+   the corresponding controls that the user might have over other forms of
+   data collection (e.g., cookies). Many general-purpose user agents
+   (i.e., Web browsers) have taken steps to reduce their fingerprints.
+</t>
+<t>
+   There are a number of request header fields that might reveal information
+   to servers that is sufficiently unique to enable fingerprinting.
+   The <x:ref>From</x:ref> header field is the most obvious, though it is
+   expected that From will only be sent when self-identification is desired by
+   the user. Likewise, Cookie header fields are deliberately designed to
+   enable re-identification, so fingerprinting concerns only apply to
+   situations where cookies are disabled or restricted by the user agent's
+   configuration.
+</t>
+<t>
+   The <x:ref>User-Agent</x:ref> header field might contain enough information
+   to uniquely identify a specific device, usually when combined with other
+   characteristics, particularly if the user agent sends excessive details
+   about the user's system or extensions. However, the source of unique
+   information that is least expected by users is
+   <x:ref>proactive negotiation</x:ref> (<xref target="proactive.negotiation"/>),
+   including the <x:ref>Accept</x:ref>, <x:ref>Accept-Charset</x:ref>,
+   <x:ref>Accept-Encoding</x:ref>, and <x:ref>Accept-Language</x:ref>
+   header fields.
+</t>
+<t>
+   In addition to the fingerprinting concern, detailed use of the
+   <x:ref>Accept-Language</x:ref> header field can reveal information the
+   user might consider to be of a private nature. For example, understanding
+   a given language set might be strongly correlated to membership in a
+   particular ethnic group.
+   An approach that limits such loss of privacy would be for a user agent
+   to omit the sending of Accept-Language except for sites that have been
+   explicitly permitted, perhaps via interaction after detecting a <x:ref>Vary</x:ref>
+   header field that indicates language negotiation might be useful.
+</t>
+<t>
+   In environments where proxies are used to enhance privacy, user agents
+   ought to be conservative in sending proactive negotiation header fields.
+   General-purpose user agents that provide a high degree of header field
+   configurability ought to inform users about the loss of privacy that might
+   result if too much detail is provided. As an extreme privacy measure,
+   proxies could filter the proactive negotiation header fields in relayed
+   requests.
+</t>
+</section>
+
+<section title="Validator Retention" anchor="security.validators">
+<t>
+   The validators defined by this specification are not intended to ensure
+   the validity of a representation, guard against malicious changes, or
+   detect on-path attacks. At best, they enable more efficient cache
+   updates and optimistic concurrent writes when all participants are behaving
+   nicely. At worst, the conditions will fail and the client will receive a
+   response that is no more harmful than an HTTP exchange without conditional
+   requests.
+</t>
+<t>
+   An entity tag can be abused in ways that create privacy risks. For example,
+   a site might deliberately construct a semantically invalid entity tag that
+   is unique to the user or user agent, send it in a cacheable response with a
+   long freshness time, and then read that entity tag in later conditional
+   requests as a means of re-identifying that user or user agent. Such an
+   identifying tag would become a persistent identifier for as long as the
+   user agent retained the original cache entry. User agents that cache
+   representations ought to ensure that the cache is cleared or replaced
+   whenever the user performs privacy-maintaining actions, such as clearing
+   stored cookies or changing to a private browsing mode.
+</t>
+</section>
+
+<section title="Denial-of-Service Attacks Using Range" anchor="overlapping.ranges">
+<t>
+   Unconstrained multiple range requests are susceptible to denial-of-service
+   attacks because the effort required to request many overlapping ranges of
+   the same data is tiny compared to the time, memory, and bandwidth consumed
+   by attempting to serve the requested data in many parts.
+   Servers ought to ignore, coalesce, or reject egregious range requests, such
+   as requests for more than two overlapping ranges or for many small ranges
+   in a single set, particularly when the ranges are requested out of order
+   for no apparent reason. Multipart range requests are not designed to
+   support random access.
+</t>
+</section>
+
+<section title="Authentication Considerations" anchor="security.auth">
+<t>
+   Everything about the topic of HTTP authentication is a security
+   consideration, so the list of considerations below is not exhaustive.
+   Furthermore, it is limited to security considerations regarding the
+   authentication framework, in general, rather than discussing all of the
+   potential considerations for specific authentication schemes (which ought
+   to be documented in the specifications that define those schemes).
+   Various organizations maintain topical information and links to current
+   research on Web application security (e.g., <xref target="OWASP"/>),
+   including common pitfalls for implementing and using the authentication
+   schemes found in practice.
+</t>
+
+<section title="Confidentiality of Credentials" anchor="confidentiality.of.credentials">
+<t>
+   The HTTP authentication framework does not define a single mechanism for
+   maintaining the confidentiality of credentials; instead, each
+   authentication scheme defines how the credentials are encoded prior to
+   transmission. While this provides flexibility for the development of future
+   authentication schemes, it is inadequate for the protection of existing
+   schemes that provide no confidentiality on their own, or that do not
+   sufficiently protect against replay attacks. Furthermore, if the server
+   expects credentials that are specific to each individual user, the exchange
+   of those credentials will have the effect of identifying that user even if
+   the content within credentials remains confidential.
+</t>
+<t>
+   HTTP depends on the security properties of the underlying transport- or
+   session-level connection to provide confidential transmission of
+   fields. Services that depend on individual user authentication require a
+   <x:ref>secured</x:ref> connection prior to exchanging credentials
+   (<xref target="https.uri"/>).
+</t>
+</section>
+
+<section title="Credentials and Idle Clients" anchor="auth.credentials.and.idle.clients">
+<t>
+   Existing HTTP clients and user agents typically retain authentication
+   information indefinitely. HTTP does not provide a mechanism for the
+   origin server to direct clients to discard these cached credentials, since
+   the protocol has no awareness of how credentials are obtained or managed
+   by the user agent. The mechanisms for expiring or revoking credentials can
+   be specified as part of an authentication scheme definition.
+</t>
+<t>
+   Circumstances under which credential caching can interfere with the
+   application's security model include but are not limited to:
+</t>
+<ul>
+   <li>Clients that have been idle for an extended period, following
+       which the server might wish to cause the client to re-prompt the
+       user for credentials.</li>
+   <li>Applications that include a session termination indication
+       (such as a "logout" or "commit" button on a page) after which
+       the server side of the application "knows" that there is no
+       further reason for the client to retain the credentials.</li>
+</ul>
+<t>
+   User agents that cache credentials are encouraged to provide a readily
+   accessible mechanism for discarding cached credentials under user control.
+</t>
+</section>
+
+<section title="Protection Spaces" anchor="protection.spaces">
+<t>
+  Authentication schemes that solely rely on the "realm" mechanism for
+  establishing a protection space will expose credentials to all resources on
+  an origin server. Clients that have successfully made authenticated requests
+  with a resource can use the same authentication credentials for other
+  resources on the same origin server. This makes it possible for a different
+  resource to harvest authentication credentials for other resources.
+</t>
+<t>
+  This is of particular concern when an origin server hosts resources for multiple
+  parties under the same origin (<xref target="protection.space"/>).
+  Possible mitigation strategies include restricting direct access to
+  authentication credentials (i.e., not making the content of the
+  <x:ref>Authorization</x:ref> request header field available), and separating protection
+  spaces by using a different host name (or port number) for each party.
+</t>
+</section>
+
+<section title="Additional Response Fields" anchor="security.auth.add.resp">
+<t>
+  Adding information to responses that are sent over an unencrypted
+  channel can affect security and privacy. The presence of the
+  <x:ref>Authentication-Info</x:ref> and <x:ref>Proxy-Authentication-Info</x:ref>
+  header fields alone indicates that HTTP authentication is in use. Additional
+  information could be exposed by the contents of the authentication-scheme
+  specific parameters; this will have to be considered in the definitions of these
+  schemes.
+</t>
+</section>
+
+</section>
+</section>
+
+<section title="IANA Considerations" anchor="IANA.considerations">
+<t>
+   The change controller for the following registrations is:
+   "IETF (iesg@ietf.org) - Internet Engineering Task Force".
+</t>
+
+<section title="URI Scheme Registration" anchor="uri.scheme.registration">
+<t>
+   IANA has updated the "Uniform Resource Identifier (URI) Schemes" registry
+   <xref target="BCP35"/> at
+   <eref target="https://www.iana.org/assignments/uri-schemes/"/> with the
+   permanent schemes listed in <xref target="uri.scheme.table"/> in <xref target="uri.schemes"/>.
+</t>
+</section>
+
+<section title="Method Registration" anchor="method.registration">
+<t>
+  IANA has updated the "Hypertext Transfer Protocol (HTTP) Method Registry" at
+  <eref target="https://www.iana.org/assignments/http-methods"/> with the
+  registration procedure of <xref target="method.registry"/> and the method
+  names summarized in the following table.
+</t>
+<?BEGININC build/draft-ietf-httpbis-semantics-latest.iana-methods ?>
+<!--AUTOGENERATED FROM extract-method-defs.xslt, do not edit manually-->
+<table anchor="iana.method.registration.table">
+   <thead>
+      <tr>
+         <th>Method</th>
+         <th>Safe</th>
+         <th>Idempotent</th>
+         <th>Section</th>
+      </tr>
+   </thead>
+   <tbody>
+      <tr>
+         <td>CONNECT</td>
+         <td>no</td>
+         <td>no</td>
+         <td>
+            <xref target="CONNECT" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>DELETE</td>
+         <td>no</td>
+         <td>yes</td>
+         <td>
+            <xref target="DELETE" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>GET</td>
+         <td>yes</td>
+         <td>yes</td>
+         <td>
+            <xref target="GET" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>HEAD</td>
+         <td>yes</td>
+         <td>yes</td>
+         <td>
+            <xref target="HEAD" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>OPTIONS</td>
+         <td>yes</td>
+         <td>yes</td>
+         <td>
+            <xref target="OPTIONS" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>POST</td>
+         <td>no</td>
+         <td>no</td>
+         <td>
+            <xref target="POST" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>PUT</td>
+         <td>no</td>
+         <td>yes</td>
+         <td>
+            <xref target="PUT" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>TRACE</td>
+         <td>yes</td>
+         <td>yes</td>
+         <td>
+            <xref target="TRACE" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>*</td>
+         <td>no</td>
+         <td>no</td>
+         <td>
+            <xref target="method.registration" format="counter"/>
+         </td>
+      </tr>
+   </tbody>
+</table>
+<!--(END)-->
+<?ENDINC build/draft-ietf-httpbis-semantics-latest.iana-methods ?>
+<t>
+   <iref primary="true" item="Method" subitem="*" x:for-anchor=""/>
+   The method name "*" is reserved because using "*" as a method name would
+   conflict with its usage as a wildcard in some fields (e.g.,
+   "Access-Control-Request-Method").
+</t>
+</section>
+
+<section title="Status Code Registration" anchor="status.code.registration">
+<t>
+   IANA has updated the "Hypertext Transfer Protocol (HTTP) Status Code Registry"
+   at <eref target="https://www.iana.org/assignments/http-status-codes"/> with
+   the registration procedure of <xref target="status.code.registry"/> and the
+   status code values summarized in the following table.
+</t>
+<?BEGININC build/draft-ietf-httpbis-semantics-latest.iana-status-codes ?>
+<!--AUTOGENERATED FROM extract-status-code-defs.xslt, do not edit manually-->
+<table anchor="iana.status.code.registration.table">
+   <thead>
+      <tr>
+         <th>Value</th>
+         <th>Description</th>
+         <th>Section</th>
+      </tr>
+   </thead>
+   <tbody>
+      <tr>
+         <td>100</td>
+         <td>Continue</td>
+         <td>
+            <xref target="status.100" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>101</td>
+         <td>Switching Protocols</td>
+         <td>
+            <xref target="status.101" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>200</td>
+         <td>OK</td>
+         <td>
+            <xref target="status.200" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>201</td>
+         <td>Created</td>
+         <td>
+            <xref target="status.201" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>202</td>
+         <td>Accepted</td>
+         <td>
+            <xref target="status.202" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>203</td>
+         <td>Non-Authoritative Information</td>
+         <td>
+            <xref target="status.203" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>204</td>
+         <td>No Content</td>
+         <td>
+            <xref target="status.204" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>205</td>
+         <td>Reset Content</td>
+         <td>
+            <xref target="status.205" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>206</td>
+         <td>Partial Content</td>
+         <td>
+            <xref target="status.206" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>300</td>
+         <td>Multiple Choices</td>
+         <td>
+            <xref target="status.300" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>301</td>
+         <td>Moved Permanently</td>
+         <td>
+            <xref target="status.301" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>302</td>
+         <td>Found</td>
+         <td>
+            <xref target="status.302" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>303</td>
+         <td>See Other</td>
+         <td>
+            <xref target="status.303" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>304</td>
+         <td>Not Modified</td>
+         <td>
+            <xref target="status.304" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>305</td>
+         <td>Use Proxy</td>
+         <td>
+            <xref target="status.305" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>306</td>
+         <td>(Unused)</td>
+         <td>
+            <xref target="status.306" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>307</td>
+         <td>Temporary Redirect</td>
+         <td>
+            <xref target="status.307" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>308</td>
+         <td>Permanent Redirect</td>
+         <td>
+            <xref target="status.308" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>400</td>
+         <td>Bad Request</td>
+         <td>
+            <xref target="status.400" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>401</td>
+         <td>Unauthorized</td>
+         <td>
+            <xref target="status.401" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>402</td>
+         <td>Payment Required</td>
+         <td>
+            <xref target="status.402" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>403</td>
+         <td>Forbidden</td>
+         <td>
+            <xref target="status.403" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>404</td>
+         <td>Not Found</td>
+         <td>
+            <xref target="status.404" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>405</td>
+         <td>Method Not Allowed</td>
+         <td>
+            <xref target="status.405" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>406</td>
+         <td>Not Acceptable</td>
+         <td>
+            <xref target="status.406" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>407</td>
+         <td>Proxy Authentication Required</td>
+         <td>
+            <xref target="status.407" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>408</td>
+         <td>Request Timeout</td>
+         <td>
+            <xref target="status.408" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>409</td>
+         <td>Conflict</td>
+         <td>
+            <xref target="status.409" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>410</td>
+         <td>Gone</td>
+         <td>
+            <xref target="status.410" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>411</td>
+         <td>Length Required</td>
+         <td>
+            <xref target="status.411" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>412</td>
+         <td>Precondition Failed</td>
+         <td>
+            <xref target="status.412" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>413</td>
+         <td>Content Too Large</td>
+         <td>
+            <xref target="status.413" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>414</td>
+         <td>URI Too Long</td>
+         <td>
+            <xref target="status.414" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>415</td>
+         <td>Unsupported Media Type</td>
+         <td>
+            <xref target="status.415" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>416</td>
+         <td>Range Not Satisfiable</td>
+         <td>
+            <xref target="status.416" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>417</td>
+         <td>Expectation Failed</td>
+         <td>
+            <xref target="status.417" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>418</td>
+         <td>(Unused)</td>
+         <td>
+            <xref target="status.418" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>421</td>
+         <td>Misdirected Request</td>
+         <td>
+            <xref target="status.421" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>422</td>
+         <td>Unprocessable Content</td>
+         <td>
+            <xref target="status.422" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>426</td>
+         <td>Upgrade Required</td>
+         <td>
+            <xref target="status.426" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>500</td>
+         <td>Internal Server Error</td>
+         <td>
+            <xref target="status.500" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>501</td>
+         <td>Not Implemented</td>
+         <td>
+            <xref target="status.501" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>502</td>
+         <td>Bad Gateway</td>
+         <td>
+            <xref target="status.502" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>503</td>
+         <td>Service Unavailable</td>
+         <td>
+            <xref target="status.503" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>504</td>
+         <td>Gateway Timeout</td>
+         <td>
+            <xref target="status.504" format="counter"/>
+         </td>
+      </tr>
+      <tr>
+         <td>505</td>
+         <td>HTTP Version Not Supported</td>
+         <td>
+            <xref target="status.505" format="counter"/>
+         </td>
+      </tr>
+   </tbody>
+</table>
+<!--(END)-->
+<?ENDINC build/draft-ietf-httpbis-semantics-latest.iana-status-codes ?>
+</section>
+
+<section title="Field Name Registration" anchor="field.name.registration">
+<t>
+   This specification updates the HTTP-related aspects of the existing
+   registration procedures for message header fields defined in <xref target="RFC3864"/>.
+   It replaces the old procedures as they relate to HTTP by defining a new
+   registration procedure and moving HTTP field definitions into a separate
+   registry.
+</t>
+<t>
+   IANA has created a new registry titled "Hypertext Transfer Protocol (HTTP)
+   Field Name Registry" as outlined in <xref target="fields.registry"/>.
+</t>
+<t>
+   IANA has moved all entries in the "Permanent Message Header Field
+   Names" and "Provisional Message Header Field Names" registries (see
+   <eref target="https://www.iana.org/assignments/message-headers/"/>) with the
+   protocol 'http' to this registry and has applied the following changes:
+</t>
+<ol>
+   <li>The 'Applicable Protocol' field has been omitted.</li>
+   <li>Entries that had a status of 'standard', 'experimental', 'reserved', or
+   'informational' have been made to have a status of 'permanent'.</li>
+   <li>Provisional entries without a status have been made to have a status of
+   'provisional'.</li>
+   <li>Permanent entries without a status (after confirmation that the
+   registration document did not define one) have been made to have a status of
+   'provisional'. The expert(s) can choose to update the entries' status if there is
+   evidence that another is more appropriate.</li>
+</ol>
+<t>
+   IANA has annotated the "Permanent Message Header Field
+   Names" and "Provisional Message Header Field Names" registries with the
+   following note to indicate that HTTP field name registrations have moved:
+</t>
+<aside>
+  <t><strong>Note</strong></t>
+  <t>
+    HTTP field name registrations have been moved to
+    [<eref target="https://www.iana.org/assignments/http-fields" brackets="none"/>] per
+    [RFC9110].
+  </t>
+</aside>
+<t>
+   IANA has updated the "Hypertext Transfer Protocol (HTTP) Field Name Registry"
+   with the field names listed in the following table.
+</t>
+<?BEGININC build/draft-ietf-httpbis-semantics-latest.iana-headers ?>
+<!--AUTOGENERATED FROM extract-header-defs.xslt, do not edit manually-->
+<table align="left" anchor="iana.header.registration.table">
+   <thead>
+      <tr>
+         <th>Field Name</th>
+         <th>Status</th>
+         <th>Section</th>
+         <th>Comments</th>
+      </tr>
+   </thead>
+   <tbody>
+      <tr>
+         <td>Accept</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.accept" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Accept-Charset</td>
+         <td>deprecated</td>
+         <td>
+            <xref target="field.accept-charset" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Accept-Encoding</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.accept-encoding" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Accept-Language</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.accept-language" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Accept-Ranges</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.accept-ranges" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Allow</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.allow" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Authentication-Info</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.authentication-info" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Authorization</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.authorization" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Connection</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.connection" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Content-Encoding</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.content-encoding" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Content-Language</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.content-language" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Content-Length</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.content-length" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Content-Location</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.content-location" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Content-Range</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.content-range" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Content-Type</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.content-type" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Date</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.date" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>ETag</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.etag" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Expect</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.expect" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>From</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.from" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Host</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.host" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>If-Match</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.if-match" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>If-Modified-Since</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.if-modified-since" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>If-None-Match</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.if-none-match" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>If-Range</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.if-range" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>If-Unmodified-Since</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.if-unmodified-since" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Last-Modified</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.last-modified" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Location</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.location" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Max-Forwards</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.max-forwards" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Proxy-Authenticate</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.proxy-authenticate" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Proxy-Authentication-Info</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.proxy-authentication-info" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Proxy-Authorization</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.proxy-authorization" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Range</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.range" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Referer</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.referer" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Retry-After</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.retry-after" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Server</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.server" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>TE</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.te" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Trailer</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.trailer" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Upgrade</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.upgrade" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>User-Agent</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.user-agent" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Vary</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.vary" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Via</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.via" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>WWW-Authenticate</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.www-authenticate" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>*</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.vary" format="counter"/>
+         </td>
+         <td>(reserved)</td>
+      </tr>
+   </tbody>
+</table>
+<!--(END)-->
+<?ENDINC build/draft-ietf-httpbis-semantics-latest.iana-headers ?>
+<t anchor="field.asterisk">
+   <iref primary="true" item="Fields" subitem="*" x:for-anchor=""/>
+   <rdf:Description>
+     <comments xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">(reserved)</comments>
+     <ref xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">field.vary</ref>
+   </rdf:Description>
+   The field name "*" is reserved because using that name as
+   an HTTP header field might conflict with its special semantics in the
+   <x:ref>Vary</x:ref> header field (<xref target="field.vary"/>).
+</t>
+<t>
+   <iref primary="true" item="Fields" subitem="Content-MD5" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Content-MD5" x:for-anchor=""/><iref primary="true" item="Content-MD5 header field"/>
+   <rdf:Description>
+     <hide-in-iana-table xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">yes</hide-in-iana-table>
+   </rdf:Description>
+   IANA has updated the "Content-MD5" entry in the new registry to have
+   a status of 'obsoleted' with references to
+   <xref target="RFC2616" x:fmt="of" x:sec="14.15"/> (for the definition
+   of the header field) and
+   <xref target="RFC7231" x:fmt="of" x:sec="B"/> (which removed the field
+   definition from the updated specification).
+</t>
+</section>
+
+<section title="Authentication Scheme Registration" anchor="auth.scheme.registration">
+<t>
+   IANA has updated the
+   "Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry"
+   at <eref target="https://www.iana.org/assignments/http-authschemes"/> with
+   the registration procedure of <xref target="auth.scheme.registry"/>.
+   No authentication schemes are defined in this document.
+</t>
+</section>
+
+<section title="Content Coding Registration" anchor="content.coding.registration">
+<t>
+   IANA has updated the "HTTP Content Coding Registry" at
+   <eref target="https://www.iana.org/assignments/http-parameters/"/>
+   with the registration procedure of <xref target="content.coding.registry"/>
+   and the content coding names summarized in the table below.
+</t>
+<table align="left" anchor="iana.content.coding.registration.table">
+  <thead>
+    <tr>
+      <th>Name</th>
+      <th>Description</th>
+      <th>Section</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>compress</td>
+      <td>UNIX "compress" data format <xref target="Welch"/></td>
+      <td><xref target="compress.coding" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>deflate</td>
+      <td>"deflate" compressed data (<xref target="RFC1951"/>) inside
+      the "zlib" data format (<xref target="RFC1950"/>)</td>
+      <td><xref target="deflate.coding" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>gzip</td>
+      <td>GZIP file format <xref target="RFC1952"/></td>
+      <td><xref target="gzip.coding" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>identity</td>
+      <td>Reserved</td>
+      <td><xref target="field.accept-encoding" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>x-compress</td>
+      <td>Deprecated (alias for compress)</td>
+      <td><xref target="compress.coding" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>x-gzip</td>
+      <td>Deprecated (alias for gzip)</td>
+      <td><xref target="gzip.coding" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+</section>
+
+<section title="Range Unit Registration" anchor="range.unit.registration">
+<t>
+   IANA has updated the "HTTP Range Unit Registry" at
+   <eref target="https://www.iana.org/assignments/http-parameters/"/>
+   with the registration procedure of <xref target="range.unit.registry"/>
+   and the range unit names summarized in the table below.
+</t>
+<table align="left" anchor="iana.range.units.table">
+  <thead>
+    <tr>
+      <th>Range Unit Name</th>
+      <th>Description</th>
+      <th>Section</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>bytes</td>
+      <td>a range of octets</td>
+      <td><xref target="byte.ranges" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>none</td>
+      <td>reserved as keyword to indicate range requests are not supported</td>
+      <td><xref target="field.accept-ranges" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+</section>
+
+<section title="Media Type Registration" anchor="media.type.reg">
+<t>
+   IANA has updated the "Media Types" registry at
+   <eref target="https://www.iana.org/assignments/media-types"/>
+   with the registration information in
+   <xref target="multipart.byteranges"/>
+   for the media type "multipart/byteranges".
+</t>
+<t>
+   IANA has updated the registry note about "q" parameters with
+   a link to <xref target="field.accept"/> of this document.
+</t>
+</section>
+
+<section title="Port Registration" anchor="port.reg">
+<t>
+   IANA has updated the "Service Name and Transport Protocol Port Number
+   Registry" at <eref target="https://www.iana.org/assignments/service-names-port-numbers/"/>
+   for the services on ports 80 and 443 that use UDP or TCP to:
+</t>
+<ol>
+   <li>use this document as "Reference", and</li>
+   <li>when currently unspecified, set "Assignee" to "IESG" and "Contact" to
+   "IETF_Chair".</li>
+</ol>
+</section>
+
+<section title="Upgrade Token Registration" anchor="upgrade.token.registration">
+<t>
+   IANA has updated the
+   "Hypertext Transfer Protocol (HTTP) Upgrade Token Registry" at
+   <eref target="https://www.iana.org/assignments/http-upgrade-tokens"/>
+   with the registration procedure described in <xref target="upgrade.token.registry"/>
+   and the upgrade token names summarized in the following table.
+</t>
+<table align="left">
+  <thead>
+    <tr>
+      <th>Name</th>
+      <th>Description</th>
+      <th>Expected Version Tokens</th>
+      <th>Section</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>HTTP</td>
+      <td>Hypertext Transfer Protocol</td>
+      <td>any DIGIT.DIGIT (e.g., "2.0")</td>
+      <td><xref target="protocol.version" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+</section>
+</section>
+</middle>
+<back>
+<displayreference target="HTTP10" to="HTTP/1.0"/>
+<displayreference target="HTTP11" to="HTTP/1.1"/>
+<displayreference target="HTTP2" to="HTTP/2"/>
+<displayreference target="HTTP3" to="HTTP/3"/>
+
+<references title="Normative References">
+
+<reference anchor="CACHING">
+  <x:source href="rfc9111.xml" basename="rfc9111"/>
+</reference>
+
+<reference anchor="RFC2046">
+  <front>
+    <title abbrev="Media Types">Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types</title>
+    <author initials="N." surname="Freed" fullname="Ned Freed"/>
+    <author initials="N." surname="Borenstein" fullname="Nathaniel S. Borenstein"/>
+    <date month="November" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="2046"/>
+</reference>
+
+<reference anchor="RFC2119">
+  <front>
+    <title>Key words for use in RFCs to Indicate Requirement Levels</title>
+    <author initials="S." surname="Bradner" fullname="Scott Bradner"/>
+    <date month="March" year="1997"/>
+  </front>
+  <seriesInfo name="BCP" value="14"/>
+  <seriesInfo name="RFC" value="2119"/>
+</reference>
+
+<reference anchor="RFC8174">
+  <front>
+    <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
+    <author initials="B." surname="Leiba" fullname="Barry Leiba"/>
+    <date year="2017" month="May"/>
+  </front>
+  <seriesInfo name="BCP" value="14"/>
+  <seriesInfo name="RFC" value="8174"/>
+</reference>
+
+<reference anchor="URI">
+ <front>
+  <title abbrev="URI Generic Syntax">Uniform Resource Identifier (URI): Generic Syntax</title>
+  <author initials="T." surname="Berners-Lee" fullname="Tim Berners-Lee"/>
+  <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+  <author initials="L." surname="Masinter" fullname="Larry Masinter"/>
+  <date month="January" year="2005"/>
+ </front>
+ <seriesInfo name="STD" value="66"/>
+ <seriesInfo name="RFC" value="3986"/>
+</reference>
+
+<reference anchor="TCP">
+  <front>
+    <title>Transmission Control Protocol</title>
+    <author initials="J." surname="Postel" fullname="Jon Postel"/>
+    <date year="1981" month="September"/>
+  </front>
+  <seriesInfo name="STD" value="7"/>
+  <seriesInfo name="RFC" value="793"/>
+</reference>
+
+<reference anchor="RFC4647">
+  <front>
+    <title>Matching of Language Tags</title>
+    <author initials="A." surname="Phillips" fullname="Addison Phillips" role="editor"/>
+    <author initials="M." surname="Davis" fullname="Mark Davis" role="editor"/>
+    <date year="2006" month="September"/>
+  </front>
+  <seriesInfo name="BCP" value="47"/>
+  <seriesInfo name="RFC" value="4647"/>
+</reference>
+
+<reference anchor="RFC4648">
+  <front>
+    <title>The Base16, Base32, and Base64 Data Encodings</title>
+    <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
+    <date year="2006" month="October"/>
+  </front>
+  <seriesInfo value="4648" name="RFC"/>
+</reference>
+
+<reference anchor="RFC5234">
+  <front>
+    <title abbrev="ABNF for Syntax Specifications">Augmented BNF for Syntax Specifications: ABNF</title>
+    <author initials="D." surname="Crocker" fullname="Dave Crocker" role="editor"/>
+    <author initials="P." surname="Overell" fullname="Paul Overell"/>
+    <date month="January" year="2008"/>
+  </front>
+  <seriesInfo name="STD" value="68"/>
+  <seriesInfo name="RFC" value="5234"/>
+</reference>
+
+<reference anchor="RFC5322">
+  <front>
+    <title>Internet Message Format</title>
+    <author initials="P." surname="Resnick" fullname="P. Resnick" role="editor"/>
+    <date year="2008" month="October"/>
+  </front>
+  <seriesInfo name="RFC" value="5322"/>
+</reference>
+
+<reference anchor="RFC5646">
+  <front>
+    <title>Tags for Identifying Languages</title>
+    <author initials="A." surname="Phillips" fullname="Addison Phillips" role="editor"/>
+    <author initials="M." surname="Davis" fullname="Mark Davis" role="editor"/>
+    <date month="September" year="2009"/>
+  </front>
+  <seriesInfo name="BCP" value="47"/>
+  <seriesInfo name="RFC" value="5646"/>
+</reference>
+
+<reference anchor="RFC6125">
+  <front>
+    <title>Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)</title>
+    <author initials="P." surname="Saint-Andre" fullname="P. Saint-Andre"/>
+    <author initials="J." surname="Hodges" fullname="J. Hodges"/>
+    <date year="2011" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="6125"/>
+</reference>
+
+<reference anchor="RFC6365">
+  <front>
+    <title>Terminology Used in Internationalization in the IETF</title>
+    <author initials="P." surname="Hoffman" fullname="P. Hoffman"/>
+    <author initials="J." surname="Klensin" fullname="J. Klensin"/>
+    <date year="2011" month="September"/>
+  </front>
+  <seriesInfo name="BCP" value="166"/>
+  <seriesInfo name="RFC" value="6365"/>
+</reference>
+
+<reference anchor="RFC7405">
+  <front>
+    <title>Case-Sensitive String Support in ABNF</title>
+    <author initials="P." surname="Kyzivat" fullname="Dave Kyzivat"/>
+    <date month="December" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7405"/>
+</reference>
+
+<reference anchor="TLS13">
+  <front>
+    <title>The Transport Layer Security (TLS) Protocol Version 1.3</title>
+    <author initials="E." surname="Rescorla" fullname="Eric Rescorla"/>
+    <date year="2018" month="August"/>
+  </front>
+  <seriesInfo name="RFC" value="8446"/>
+</reference>
+
+<reference anchor="USASCII">
+  <front>
+    <title>Coded Character Set -- 7-bit American Standard Code for Information Interchange</title>
+    <author>
+      <organization>American National Standards Institute</organization>
+    </author>
+    <date year="1986"/>
+  </front>
+  <seriesInfo name="ANSI" value="X3.4"/>
+</reference>
+
+<reference anchor="RFC1950">
+  <front>
+    <title>ZLIB Compressed Data Format Specification version 3.3</title>
+    <author initials="P." surname="Deutsch" fullname="L. Peter Deutsch"/>
+    <author initials="J-L." surname="Gailly" fullname="Jean-Loup Gailly"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1950"/>
+</reference>
+
+<reference anchor="RFC1951">
+  <front>
+    <title>DEFLATE Compressed Data Format Specification version 1.3</title>
+    <author initials="P." surname="Deutsch" fullname="L. Peter Deutsch"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1951"/>
+</reference>
+
+<reference anchor="RFC1952">
+  <front>
+    <title>GZIP file format specification version 4.3</title>
+    <author initials="P." surname="Deutsch" fullname="L. Peter Deutsch"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1952"/>
+</reference>
+
+<reference anchor="Welch" target="https://ieeexplore.ieee.org/document/1659158/">
+  <front>
+    <title>A Technique for High-Performance Data Compression</title>
+    <author initials="T." surname="Welch" fullname="Terry A. Welch"/>
+    <date month="June" year="1984"/>
+  </front>
+  <seriesInfo name="IEEE Computer" value="17(6)"/>
+  <seriesInfo name="DOI" value="10.1109/MC.1984.1659158"/>
+</reference>
+
+<reference anchor="RFC5280">
+  <front>
+    <title>Internet X.509 Public Key Infrastructure Certificate and
+           Certificate Revocation List (CRL) Profile</title>
+    <author initials="D." surname="Cooper" fullname="D. Cooper"/>
+    <author initials="S." surname="Santesson" fullname="S. Santesson"/>
+    <author initials="S." surname="Farrell" fullname="S. Farrell"/>
+    <author initials="S." surname="Boeyen" fullname="S. Boeyen"/>
+    <author initials="R." surname="Housley" fullname="R. Housley"/>
+    <author initials="W." surname="Polk" fullname="W. Polk"/>
+    <date year="2008" month="May"/>
+  </front>
+  <seriesInfo name="RFC" value="5280"/>
+</reference>
+
+</references>
+
+<references title="Informative References">
+
+<reference anchor="HTTP11">
+  <x:source href="rfc9112.xml" basename="rfc9112"/>
+</reference>
+
+<reference anchor="Err1912" target="https://www.rfc-editor.org/errata/eid1912" quoteTitle="false">
+  <front>
+    <title>Erratum ID 1912</title>
+    <author>
+      <organization>RFC Errata</organization>
+    </author>
+    <date/>
+  </front>
+  <refcontent>RFC 2978</refcontent>
+</reference>
+
+<reference anchor="Err5433" target="https://www.rfc-editor.org/errata/eid5433" quoteTitle="false">
+  <front>
+    <title>Erratum ID 5433</title>
+    <author>
+      <organization>RFC Errata</organization>
+    </author>
+    <date/>
+  </front>
+  <refcontent>RFC 2978</refcontent>
+</reference>
+
+<reference anchor="BREACH" target="http://breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf">
+   <front>
+      <title>BREACH: Reviving the CRIME Attack</title>
+      <author initials="Y." surname="Gluck" fullname="Yoel Gluck"/>
+      <author initials="N." surname="Harris" fullname="Neal Harris"/>
+      <author initials="A." surname="Prado" fullname="Angelo Prado"/>
+      <date year="2013" month="July"/>
+   </front>
+</reference>
+
+<reference anchor="Bujlow">
+  <front>
+    <title>A Survey on Web Tracking: Mechanisms, Implications, and Defenses</title>
+    <author initials="T." surname="Bujlow" fullname="Tomasz Bujlow"/>
+    <author initials="V." surname="Carela-Español" fullname="Valentin Carela-Español"/>
+    <author initials="J." surname="Solé-Pareta" fullname="Josep Solé-Pareta"/>
+    <author initials="P." surname="Barlet-Ros" fullname="Pere Barlet-Ros"/>
+    <date year="2017" month="August"/>
+  </front>
+  <seriesInfo name="DOI" value="10.1109/JPROC.2016.2637878"/>
+  <refcontent>In Proceedings of the IEEE 105(8)</refcontent>
+</reference>
+
+<reference anchor="Georgiev">
+  <front>
+    <title>The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software</title>
+    <author initials="M." surname="Georgiev" fullname="Martin Georgiev"/>
+    <author initials="S." surname="Iyengar" fullname="Subodh Iyengar"/>
+    <author initials="S." surname="Jana" fullname="Suman Jana"/>
+    <author initials="R." surname="Anubhai" fullname="Rishita Anubhai"/>
+    <author initials="D." surname="Boneh" fullname="Dan Boneh"/>
+    <author initials="V." surname="Shmatikov" fullname="Vitaly Shmatikov"/>
+    <date year="2012" month="October"/>
+  </front>
+  <seriesInfo name="DOI" value="10.1145/2382196.2382204"/>
+  <refcontent>In Proceedings of the 2012 ACM Conference on Computer and Communications Security (CCS '12), pp. 38-49</refcontent>
+</reference>
+
+<reference anchor="ISO-8859-1">
+  <front>
+    <title>
+     Information technology -- 8-bit single-byte coded graphic character sets -- Part 1: Latin alphabet No. 1
+    </title>
+    <author>
+      <organization>International Organization for Standardization</organization>
+    </author>
+    <date year="1998"/>
+  </front>
+  <seriesInfo name="ISO/IEC" value="8859-1:1998"/>
+</reference>
+
+<reference anchor="Kri2001" target="http://arxiv.org/abs/cs.SE/0105018">
+  <front>
+    <title>HTTP Cookies: Standards, Privacy, and Politics</title>
+    <author initials="D." surname="Kristol" fullname="David M. Kristol"/>
+    <date year="2001" month="November"/>
+  </front>
+  <seriesInfo name="ACM Transactions on Internet Technology" value="1(2)"/>
+</reference>
+
+<reference anchor="Sniffing" target="https://mimesniff.spec.whatwg.org">
+   <front>
+      <title>MIME Sniffing</title>
+      <author>
+         <organization>WHATWG</organization>
+      </author>
+      <date/>
+   </front>
+</reference>
+
+<reference anchor="REST" target="https://roy.gbiv.com/pubs/dissertation/top.htm">
+  <front>
+    <title>Architectural Styles and the Design of Network-based Software Architectures</title>
+    <author initials="R.T." surname="Fielding" fullname="Roy T. Fielding"/>
+    <date month="September" year="2000"/>
+  </front>
+  <refcontent>Doctoral Dissertation, University of California, Irvine</refcontent>
+</reference>
+
+<reference anchor="RFC1919">
+  <front>
+    <title>Classical versus Transparent IP Proxies</title>
+    <author initials="M." surname="Chatel" fullname="Marc Chatel"/>
+    <date year="1996" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="1919"/>
+</reference>
+
+<reference anchor="HTTP10">
+  <front>
+    <title abbrev="HTTP/1.0">Hypertext Transfer Protocol -- HTTP/1.0</title>
+    <author initials="T." surname="Berners-Lee" fullname="Tim Berners-Lee"/>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+    <author initials="H." surname="Frystyk" fullname="Henrik Frystyk Nielsen"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1945"/>
+</reference>
+
+<reference anchor="RFC2047">
+  <front>
+    <title abbrev="Message Header Extensions">MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text</title>
+    <author initials="K." surname="Moore" fullname="Keith Moore"/>
+    <date month="November" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="2047"/>
+</reference>
+
+<reference anchor="RFC2068">
+  <front>
+    <title>Hypertext Transfer Protocol -- HTTP/1.1</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+    <author initials="J." surname="Gettys" fullname="Jim Gettys"/>
+    <author initials="J." surname="Mogul" fullname="Jeffrey C. Mogul"/>
+    <author initials="H." surname="Frystyk" fullname="Henrik Frystyk Nielsen"/>
+    <author initials="T." surname="Berners-Lee" fullname="Tim Berners-Lee"/>
+    <date month="January" year="1997"/>
+  </front>
+  <seriesInfo name="RFC" value="2068"/>
+</reference>
+
+<reference anchor="RFC2145">
+  <front>
+    <title abbrev="HTTP Version Numbers">Use and Interpretation of HTTP Version Numbers</title>
+    <author initials="J. C." surname="Mogul" fullname="Jeffrey C. Mogul"/>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+    <author initials="J." surname="Gettys" fullname="Jim Gettys"/>
+    <author initials="H." surname="Frystyk" fullname="Henrik Frystyk Nielsen"/>
+    <date month="May" year="1997"/>
+  </front>
+  <seriesInfo name="RFC" value="2145"/>
+</reference>
+
+<reference anchor="RFC2295">
+  <front>
+    <title abbrev="HTTP Content Negotiation">Transparent Content Negotiation in HTTP</title>
+    <author initials="K." surname="Holtman" fullname="Koen Holtman"/>
+    <author initials="A." surname="Mutz" fullname="Andrew H. Mutz"/>
+    <date year="1998" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="2295"/>
+</reference>
+
+<reference anchor="RFC2324">
+  <front>
+    <title>Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)</title>
+    <author initials="L." surname="Masinter" fullname="L. Masinter"/>
+    <date year="1998" month="April" day="1" x:include-day="true"/>
+  </front>
+  <seriesInfo name="RFC" value="2324"/>
+</reference>
+
+<reference anchor="RFC2557">
+  <front>
+    <title abbrev="MIME Encapsulation of Aggregate Documents">MIME Encapsulation of Aggregate Documents, such as HTML (MHTML)</title>
+    <author initials="J." surname="Palme" fullname="Jacob Palme"/>
+    <author initials="A." surname="Hopmann" fullname="Alex Hopmann"/>
+    <author initials="N." surname="Shelness" fullname="Nick Shelness"/>
+    <date year="1999" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="2557"/>
+</reference>
+
+<reference anchor="RFC2616">
+  <front>
+    <title>Hypertext Transfer Protocol -- HTTP/1.1</title>
+    <author initials="R." surname="Fielding" fullname="R. Fielding"/>
+    <author initials="J." surname="Gettys" fullname="J. Gettys"/>
+    <author initials="J." surname="Mogul" fullname="J. Mogul"/>
+    <author initials="H." surname="Frystyk" fullname="H. Frystyk"/>
+    <author initials="L." surname="Masinter" fullname="L. Masinter"/>
+    <author initials="P." surname="Leach" fullname="P. Leach"/>
+    <author initials="T." surname="Berners-Lee" fullname="T. Berners-Lee"/>
+    <date month="June" year="1999"/>
+  </front>
+  <seriesInfo name="RFC" value="2616"/>
+</reference>
+
+<reference anchor="RFC2617">
+  <front>
+    <title abbrev="HTTP Authentication">HTTP Authentication: Basic and Digest Access Authentication</title>
+    <author initials="J." surname="Franks" fullname="John Franks"/>
+    <author initials="P." surname="Hallam-Baker" fullname="Phillip M. Hallam-Baker"/>
+    <author initials="J." surname="Hostetler" fullname="Jeffery L. Hostetler"/>
+    <author initials="S." surname="Lawrence" fullname="Scott D. Lawrence"/>
+    <author initials="P." surname="Leach" fullname="Paul J. Leach"/>
+    <author initials="A." surname="Luotonen" fullname="Ari Luotonen"/>
+    <author initials="L." surname="Stewart" fullname="Lawrence C. Stewart"/>
+    <date month="June" year="1999"/>
+  </front>
+  <seriesInfo name="RFC" value="2617"/>
+</reference>
+
+<reference anchor="RFC2774">
+  <front>
+    <title>An HTTP Extension Framework</title>
+    <author initials="H." surname="Nielsen" fullname="Henrik Frystyk Nielsen"/>
+    <author initials="P." surname="Leach" fullname="Paul J. Leach"/>
+    <author initials="S." surname="Lawrence" fullname="Scott Lawrence"/>
+    <date year="2000" month="February"/>
+  </front>
+  <seriesInfo name="RFC" value="2774"/>
+</reference>
+
+<reference anchor="RFC2818">
+  <front>
+    <title>HTTP Over TLS</title>
+    <author initials="E." surname="Rescorla" fullname="Eric Rescorla"/>
+    <date year="2000" month="May"/>
+  </front>
+  <seriesInfo name="RFC" value="2818"/>
+</reference>
+
+<reference anchor="RFC2978">
+  <front>
+    <title>IANA Charset Registration Procedures</title>
+    <author initials="N." surname="Freed" fullname="N. Freed"/>
+    <author initials="J." surname="Postel" fullname="J. Postel"/>
+    <date year="2000" month="October"/>
+  </front>
+  <seriesInfo name="BCP" value="19"/>
+  <seriesInfo name="RFC" value="2978"/>
+</reference>
+
+<reference anchor="RFC3040">
+  <front>
+    <title>Internet Web Replication and Caching Taxonomy</title>
+    <author initials="I." surname="Cooper" fullname="I. Cooper"/>
+    <author initials="I." surname="Melve" fullname="I. Melve"/>
+    <author initials="G." surname="Tomlinson" fullname="G. Tomlinson"/>
+    <date year="2001" month="January"/>
+  </front>
+  <seriesInfo name="RFC" value="3040"/>
+</reference>
+
+<reference anchor="RFC4033">
+  <front>
+    <title>DNS Security Introduction and Requirements</title>
+    <author initials="R." surname="Arends" fullname="R. Arends"/>
+    <author initials="R." surname="Austein" fullname="R. Austein"/>
+    <author initials="M." surname="Larson" fullname="M. Larson"/>
+    <author initials="D." surname="Massey" fullname="D. Massey"/>
+    <author initials="S." surname="Rose" fullname="S. Rose"/>
+    <date year="2005" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="4033"/>
+</reference>
+
+<reference anchor="RFC4559">
+  <front>
+    <title>SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows</title>
+    <author initials="K." surname="Jaganathan" fullname="K. Jaganathan"/>
+    <author initials="L." surname="Zhu" fullname="L. Zhu"/>
+    <author initials="J." surname="Brezak" fullname="J. Brezak"/>
+    <date year="2006" month="June"/>
+  </front>
+  <seriesInfo name="RFC" value="4559"/>
+</reference>
+
+<reference anchor="WEBDAV">
+  <front>
+    <title>HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)</title>
+    <author initials="L." surname="Dusseault" fullname="Lisa Dusseault" role="editor"/>
+    <date month="June" year="2007"/>
+  </front>
+  <seriesInfo name="RFC" value="4918"/>
+</reference>
+
+<reference anchor="HTTP2">
+   <front>
+      <title>HTTP/2</title>
+      <author initials="M." surname="Thomson" fullname="Martin Thomson" role="editor"/>
+      <author initials="C." surname="Benfield" fullname="Cory Benfield" role="editor"/>
+      <date year="2022" month="June"/>
+   </front>
+   <seriesInfo name="RFC" value="9113"/>
+</reference>
+
+<reference anchor="HPACK">
+   <front>
+      <title>HPACK: Header Compression for HTTP/2</title>
+      <author initials="R." surname="Peon" fullname="R. Peon"/>
+      <author initials="H." surname="Ruellan" fullname="H. Ruellan"/>
+      <date year="2015" month="May"/>
+   </front>
+   <seriesInfo name="RFC" value="7541"/>
+</reference>
+
+<reference anchor="RFC5905">
+  <front>
+    <title>Network Time Protocol Version 4: Protocol and Algorithms Specification</title>
+    <author initials="D." surname="Mills" fullname="David L. Mills"/>
+    <author initials="J." surname="Martin" fullname="Jim Martin" role="editor"/>
+    <author initials="J." surname="Burbank" fullname="Jack Burbank"/>
+    <author initials="W." surname="Kasch" fullname="William Kasch"/>
+    <date year="2010" month="June"/>
+  </front>
+  <seriesInfo name="RFC" value="5905"/>
+</reference>
+
+<reference anchor="RFC6454">
+  <front>
+    <title>The Web Origin Concept</title>
+    <author initials="A." surname="Barth" fullname="A. Barth"/>
+    <date year="2011" month="December"/>
+  </front>
+  <seriesInfo name="RFC" value="6454"/>
+</reference>
+
+<reference anchor="RFC7230">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7230"/>
+</reference>
+
+<reference anchor="RFC7231">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7231"/>
+</reference>
+
+<reference anchor="RFC7232">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests</title>
+    <author fullname="Roy T. Fielding" initials="R." role="editor" surname="Fielding"/>
+    <author fullname="Julian F. Reschke" initials="J." role="editor" surname="Reschke"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7232"/>
+</reference>
+
+<reference anchor="RFC7233">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Range Requests</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+    <author initials="Y." surname="Lafon" fullname="Yves Lafon" role="editor"/>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7233"/>
+</reference>
+
+<reference anchor="RFC7234">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Caching</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+    <author initials="M." surname="Nottingham" fullname="Mark Nottingham" role="editor"/>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7234"/>
+</reference>
+
+<reference anchor="RFC7235">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Authentication</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7235"/>
+</reference>
+
+<reference anchor="RFC7578">
+  <front>
+    <title>Returning Values from Forms: multipart/form-data</title>
+    <author initials="L." surname="Masinter" fullname="Larry Masinter"/>
+    <date year="2015" month="July"/>
+  </front>
+  <seriesInfo name="RFC" value="7578"/>
+</reference>
+
+<reference anchor="RFC7615">
+  <front>
+    <title>HTTP Authentication-Info and Proxy-Authentication-Info Response Header Fields</title>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
+    <date year="2015" month="September"/>
+    </front>
+  <seriesInfo name="RFC" value="7615"/>
+</reference>
+
+<reference anchor="ALTSVC">
+  <front>
+    <title>HTTP Alternative Services</title>
+    <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
+    <author initials="P." surname="McManus" fullname="P. McManus"/>
+    <author initials="J." surname="Reschke" fullname="J. Reschke"/>
+    <date year="2016" month="April"/>
+  </front>
+  <seriesInfo name="RFC" value="7838"/>
+</reference>
+
+<reference anchor="RFC8336">
+  <front>
+    <title>The ORIGIN HTTP/2 Frame</title>
+    <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
+    <author initials="E." surname="Nygren" fullname="E. Nygren"/>
+    <date year="2018" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="8336"/>
+</reference>
+
+<reference anchor="RFC8615">
+   <front>
+      <title>Well-Known Uniform Resource Identifiers (URIs)</title>
+      <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
+      <date year="2019" month="May"/>
+   </front>
+   <seriesInfo name="RFC" value="8615"/>
+</reference>
+
+<reference anchor="HTTP3">
+  <front>
+    <title>HTTP/3</title>
+    <author initials="M." surname="Bishop" fullname="Mike Bishop" role="editor"/>
+    <date year="2022" month="June"/>
+  </front>
+  <seriesInfo name="RFC" value="9114"/>
+</reference>
+
+<referencegroup anchor="BCP13" target="https://www.rfc-editor.org/info/bcp13">
+  <reference anchor="RFC4289">
+    <front>
+      <title>Multipurpose Internet Mail Extensions (MIME) Part Four: Registration Procedures</title>
+      <author initials="N." surname="Freed" fullname="Ned Freed"/>
+      <author initials="J." surname="Klensin" fullname="John C. Klensin"/>
+      <date year="2005" month="December"/>
+    </front>
+    <seriesInfo name="BCP" value="13"/>
+    <seriesInfo name="RFC" value="4289"/>
+  </reference>
+  <reference anchor="RFC6838">
+    <front>
+      <title>Media Type Specifications and Registration Procedures</title>
+      <author initials="N." surname="Freed" fullname="Ned Freed"/>
+      <author initials="J." surname="Klensin" fullname="John C. Klensin"/>
+      <author initials="T." surname="Hansen" fullname="Tony Hansen"/>
+      <date year="2013" month="January"/>
+    </front>
+    <seriesInfo name="BCP" value="13"/>
+    <seriesInfo name="RFC" value="6838"/>
+  </reference>
+</referencegroup>
+
+<referencegroup anchor="BCP35" target="https://www.rfc-editor.org/info/bcp35">
+  <reference anchor="RFC7595">
+    <front>
+      <title>Guidelines and Registration Procedures for URI Schemes</title>
+      <author initials="D." surname="Thaler" fullname="Dave Thaler" role="editor"/>
+      <author initials="T." surname="Hansen" fullname="Tony Hansen"/>
+      <author initials="T." surname="Hardie" fullname="Ted Hardie"/>
+      <date year="2015" month="June"/>
+    </front>
+    <seriesInfo name="BCP" value="35"/>
+    <seriesInfo name="RFC" value="7595"/>
+  </reference>
+</referencegroup>
+
+<referencegroup anchor="BCP178" target="https://www.rfc-editor.org/info/bcp178">
+  <reference anchor="RFC6648">
+    <front>
+      <title>Deprecating the "X-" Prefix and Similar Constructs in Application Protocols</title>
+      <author initials="P." surname="Saint-Andre" fullname="Peter Saint-Andre"/>
+      <author initials="D." surname="Crocker" fullname="Dave Crocker"/>
+      <author initials="M." surname="Nottingham" fullname="Mark Nottingham"/>
+      <date year="2012" month="June"/>
+    </front>
+    <seriesInfo name="BCP" value="178"/>
+    <seriesInfo name="RFC" value="6648"/>
+  </reference>
+</referencegroup>
+
+<reference anchor="RFC3864">
+  <front>
+    <title>Registration Procedures for Message Header Fields</title>
+    <author initials="G." surname="Klyne" fullname="G. Klyne"/>
+    <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
+    <author initials="J." surname="Mogul" fullname="J. Mogul"/>
+    <date year="2004" month="September"/>
+  </front>
+  <seriesInfo name="BCP" value="90"/>
+  <seriesInfo name="RFC" value="3864"/>
+</reference>
+
+<reference anchor="RFC3875">
+  <front>
+    <title>The Common Gateway Interface (CGI) Version 1.1</title>
+    <author initials="D." surname="Robinson" fullname="David Robinson"/>
+    <author initials="K." surname="Coar" fullname="Ken A. L. Coar"/>
+    <date year="2004" month="October"/>
+  </front>
+  <seriesInfo name="RFC" value="3875"/>
+</reference>
+
+<reference anchor="RFC5789">
+  <front>
+    <title>PATCH Method for HTTP</title>
+    <author initials="L." surname="Dusseault" fullname="L. Dusseault"/>
+    <author initials="J." surname="Snell" fullname="J. Snell"/>
+    <date year="2010" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="5789"/>
+</reference>
+
+<reference anchor="COOKIE">
+  <front>
+    <title>HTTP State Management Mechanism</title>
+    <author initials="A." surname="Barth" fullname="Adam Barth"/>
+    <date year="2011" month="April"/>
+  </front>
+  <seriesInfo name="RFC" value="6265"/>
+</reference>
+
+<reference anchor="RFC6585">
+  <front>
+    <title>Additional HTTP Status Codes</title>
+    <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
+    <author initials="R." surname="Fielding" fullname="R. Fielding"/>
+    <date year="2012" month="April"/>
+   </front>
+   <seriesInfo name="RFC" value="6585"/>
+</reference>
+
+<reference anchor="RFC7538">
+	<front>
+    <title>The Hypertext Transfer Protocol Status Code 308 (Permanent Redirect)</title>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
+    <date month="April" year="2015"/>
+  </front>
+  <seriesInfo name="RFC" value="7538"/>
+</reference>
+
+<reference anchor="RFC7540">
+   <front>
+      <title>Hypertext Transfer Protocol Version 2 (HTTP/2)</title>
+      <author initials="M." surname="Belshe" fullname="M. Belshe"/>
+      <author initials="R." surname="Peon" fullname="R. Peon"/>
+      <author initials="M." surname="Thomson" fullname="M. Thomson" role="editor"/>
+      <date year="2015" month="May"/>
+   </front>
+   <seriesInfo name="RFC" value="7540"/>
+</reference>
+
+<reference anchor="RFC7616">
+  <front>
+    <title>HTTP Digest Access Authentication</title>
+    <author initials="R." surname="Shekh-Yusef" fullname="R. Shekh-Yusef" role="editor"/>
+    <author initials="D." surname="Ahrens" fullname="D. Ahrens"/>
+    <author initials="S." surname="Bremer" fullname="S. Bremer"/>
+    <date year="2015" month="September"/>
+  </front>
+  <seriesInfo name="RFC" value="7616"/>
+</reference>
+
+<reference anchor="RFC7617">
+  <front>
+    <title>The 'Basic' HTTP Authentication Scheme</title>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
+    <date year="2015" month="September"/>
+  </front>
+  <seriesInfo name="RFC" value="7617"/>
+</reference>
+
+<reference anchor="RFC7694">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP) Client-Initiated Content-Encoding</title>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
+    <date year="2015" month="November"/>
+  </front>
+  <seriesInfo name="RFC" value="7694"/>
+</reference>
+
+<reference anchor="RFC8126">
+  <front>
+    <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
+    <author initials="M." surname="Cotton" fullname="M. Cotton"/>
+    <author initials="B." surname="Leiba" fullname="B. Leiba"/>
+    <author initials="T." surname="Narten" fullname="T. Narten"/>
+    <date year="2017" month="June"/>
+  </front>
+  <seriesInfo name="BCP" value="26"/>
+  <seriesInfo name="RFC" value="8126"/>
+</reference>
+
+<reference anchor="RFC8187">
+	<front>
+    <title>Indicating Character Encoding and Language for HTTP Header Field Parameters</title>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke"/>
+    <date month="September" year="2017"/>
+  </front>
+  <seriesInfo name="RFC" value="8187"/>
+</reference>
+
+<reference anchor="RFC8246">
+  <front>
+    <title>HTTP Immutable Responses</title>
+    <author initials="P." surname="McManus" fullname="P. McManus"/>
+    <date year="2017" month="September"/>
+  </front>
+  <seriesInfo name="RFC" value="8246"/>
+</reference>
+
+<reference anchor="RFC8288">
+  <front>
+    <title>Web Linking</title>
+    <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
+    <date year="2017" month="October"/>
+  </front>
+  <seriesInfo name="RFC" value="8288"/>
+</reference>
+
+<reference anchor="RFC8941">
+  <front>
+    <title>Structured Field Values for HTTP</title>
+    <author initials="M." surname="Nottingham" fullname="Mark Nottingham"/>
+    <author initials="P-H." surname="Kamp" fullname="Poul-Henning Kamp"/>
+    <date month="February" year="2021"/>
+  </front>
+  <seriesInfo name="RFC" value="8941"/>
+</reference>
+
+<reference anchor="OWASP" target="https://www.owasp.org/" quoteTitle="false">
+	<front>
+    <title>The Open Web Application Security Project</title>
+    <author><organization/></author>
+  </front>
+</reference>
+</references>
+
+<?BEGININC build/draft-ietf-httpbis-semantics-latest.abnf-appendix ?>
+<section title="Collected ABNF" anchor="collected.abnf"><t>In the collected ABNF below, list rules are expanded per <xref target="abnf.extension"/>.</t><sourcecode type="abnf" name="rfc9110.parsed-abnf">
+<x:ref>Accept</x:ref> = [ ( media-range [ weight ] ) *( OWS "," OWS ( media-range [
+ weight ] ) ) ]
+<x:ref>Accept-Charset</x:ref> = [ ( ( token / "*" ) [ weight ] ) *( OWS "," OWS ( (
+ token / "*" ) [ weight ] ) ) ]
+<x:ref>Accept-Encoding</x:ref> = [ ( codings [ weight ] ) *( OWS "," OWS ( codings [
+ weight ] ) ) ]
+<x:ref>Accept-Language</x:ref> = [ ( language-range [ weight ] ) *( OWS "," OWS (
+ language-range [ weight ] ) ) ]
+<x:ref>Accept-Ranges</x:ref> = acceptable-ranges
+<x:ref>Allow</x:ref> = [ method *( OWS "," OWS method ) ]
+<x:ref>Authentication-Info</x:ref> = [ auth-param *( OWS "," OWS auth-param ) ]
+<x:ref>Authorization</x:ref> = credentials
+
+<x:ref>BWS</x:ref> = OWS
+
+<x:ref>Connection</x:ref> = [ connection-option *( OWS "," OWS connection-option )
+ ]
+<x:ref>Content-Encoding</x:ref> = [ content-coding *( OWS "," OWS content-coding )
+ ]
+<x:ref>Content-Language</x:ref> = [ language-tag *( OWS "," OWS language-tag ) ]
+<x:ref>Content-Length</x:ref> = 1*DIGIT
+<x:ref>Content-Location</x:ref> = absolute-URI / partial-URI
+<x:ref>Content-Range</x:ref> = range-unit SP ( range-resp / unsatisfied-range )
+<x:ref>Content-Type</x:ref> = media-type
+
+<x:ref>Date</x:ref> = HTTP-date
+
+<x:ref>ETag</x:ref> = entity-tag
+<x:ref>Expect</x:ref> = [ expectation *( OWS "," OWS expectation ) ]
+
+<x:ref>From</x:ref> = mailbox
+
+<x:ref>GMT</x:ref> = %x47.4D.54 ; GMT
+
+<x:ref>HTTP-date</x:ref> = IMF-fixdate / obs-date
+<x:ref>Host</x:ref> = uri-host [ ":" port ]
+
+<x:ref>IMF-fixdate</x:ref> = day-name "," SP date1 SP time-of-day SP GMT
+<x:ref>If-Match</x:ref> = "*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
+<x:ref>If-Modified-Since</x:ref> = HTTP-date
+<x:ref>If-None-Match</x:ref> = "*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
+<x:ref>If-Range</x:ref> = entity-tag / HTTP-date
+<x:ref>If-Unmodified-Since</x:ref> = HTTP-date
+
+<x:ref>Last-Modified</x:ref> = HTTP-date
+<x:ref>Location</x:ref> = URI-reference
+
+<x:ref>Max-Forwards</x:ref> = 1*DIGIT
+
+<x:ref>OWS</x:ref> = *( SP / HTAB )
+
+<x:ref>Proxy-Authenticate</x:ref> = [ challenge *( OWS "," OWS challenge ) ]
+<x:ref>Proxy-Authentication-Info</x:ref> = [ auth-param *( OWS "," OWS auth-param )
+ ]
+<x:ref>Proxy-Authorization</x:ref> = credentials
+
+<x:ref>RWS</x:ref> = 1*( SP / HTAB )
+<x:ref>Range</x:ref> = ranges-specifier
+<x:ref>Referer</x:ref> = absolute-URI / partial-URI
+<x:ref>Retry-After</x:ref> = HTTP-date / delay-seconds
+
+<x:ref>Server</x:ref> = product *( RWS ( product / comment ) )
+
+<x:ref>TE</x:ref> = [ t-codings *( OWS "," OWS t-codings ) ]
+<x:ref>Trailer</x:ref> = [ field-name *( OWS "," OWS field-name ) ]
+
+<x:ref>URI-reference</x:ref> = &lt;URI-reference, see <xref target="URI" x:fmt="," x:sec="4.1"/>&gt;
+<x:ref>Upgrade</x:ref> = [ protocol *( OWS "," OWS protocol ) ]
+<x:ref>User-Agent</x:ref> = product *( RWS ( product / comment ) )
+
+<x:ref>Vary</x:ref> = [ ( "*" / field-name ) *( OWS "," OWS ( "*" / field-name ) )
+ ]
+<x:ref>Via</x:ref> = [ ( received-protocol RWS received-by [ RWS comment ] ) *( OWS
+ "," OWS ( received-protocol RWS received-by [ RWS comment ] ) ) ]
+
+<x:ref>WWW-Authenticate</x:ref> = [ challenge *( OWS "," OWS challenge ) ]
+
+<x:ref>absolute-URI</x:ref> = &lt;absolute-URI, see <xref target="URI" x:fmt="," x:sec="4.3"/>&gt;
+<x:ref>absolute-path</x:ref> = 1*( "/" segment )
+<x:ref>acceptable-ranges</x:ref> = range-unit *( OWS "," OWS range-unit )
+<x:ref>asctime-date</x:ref> = day-name SP date3 SP time-of-day SP year
+<x:ref>auth-param</x:ref> = token BWS "=" BWS ( token / quoted-string )
+<x:ref>auth-scheme</x:ref> = token
+<x:ref>authority</x:ref> = &lt;authority, see <xref target="URI" x:fmt="," x:sec="3.2"/>&gt;
+
+<x:ref>challenge</x:ref> = auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS ","
+ OWS auth-param ) ] ) ]
+<x:ref>codings</x:ref> = content-coding / "identity" / "*"
+<x:ref>comment</x:ref> = "(" *( ctext / quoted-pair / comment ) ")"
+<x:ref>complete-length</x:ref> = 1*DIGIT
+<x:ref>connection-option</x:ref> = token
+<x:ref>content-coding</x:ref> = token
+<x:ref>credentials</x:ref> = auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS ","
+ OWS auth-param ) ] ) ]
+<x:ref>ctext</x:ref> = HTAB / SP / %x21-27 ; '!'-'''
+ / %x2A-5B ; '*'-'['
+ / %x5D-7E ; ']'-'~'
+ / obs-text
+
+<x:ref>date1</x:ref> = day SP month SP year
+<x:ref>date2</x:ref> = day "-" month "-" 2DIGIT
+<x:ref>date3</x:ref> = month SP ( 2DIGIT / ( SP DIGIT ) )
+<x:ref>day</x:ref> = 2DIGIT
+<x:ref>day-name</x:ref> = %x4D.6F.6E ; Mon
+ / %x54.75.65 ; Tue
+ / %x57.65.64 ; Wed
+ / %x54.68.75 ; Thu
+ / %x46.72.69 ; Fri
+ / %x53.61.74 ; Sat
+ / %x53.75.6E ; Sun
+<x:ref>day-name-l</x:ref> = %x4D.6F.6E.64.61.79 ; Monday
+ / %x54.75.65.73.64.61.79 ; Tuesday
+ / %x57.65.64.6E.65.73.64.61.79 ; Wednesday
+ / %x54.68.75.72.73.64.61.79 ; Thursday
+ / %x46.72.69.64.61.79 ; Friday
+ / %x53.61.74.75.72.64.61.79 ; Saturday
+ / %x53.75.6E.64.61.79 ; Sunday
+<x:ref>delay-seconds</x:ref> = 1*DIGIT
+
+<x:ref>entity-tag</x:ref> = [ weak ] opaque-tag
+<x:ref>etagc</x:ref> = "!" / %x23-7E ; '#'-'~'
+ / obs-text
+<x:ref>expectation</x:ref> = token [ "=" ( token / quoted-string ) parameters ]
+
+<x:ref>field-content</x:ref> = field-vchar [ 1*( SP / HTAB / field-vchar )
+ field-vchar ]
+<x:ref>field-name</x:ref> = token
+<x:ref>field-value</x:ref> = *field-content
+<x:ref>field-vchar</x:ref> = VCHAR / obs-text
+<x:ref>first-pos</x:ref> = 1*DIGIT
+
+<x:ref>hour</x:ref> = 2DIGIT
+<x:ref>http-URI</x:ref> = "http://" authority path-abempty [ "?" query ]
+<x:ref>https-URI</x:ref> = "https://" authority path-abempty [ "?" query ]
+
+<x:ref>incl-range</x:ref> = first-pos "-" last-pos
+<x:ref>int-range</x:ref> = first-pos "-" [ last-pos ]
+
+<x:ref>language-range</x:ref> = &lt;language-range, see <xref target="RFC4647" x:fmt="," x:sec="2.1"/>&gt;
+<x:ref>language-tag</x:ref> = &lt;Language-Tag, see <xref target="RFC5646" x:fmt="," x:sec="2.1"/>&gt;
+<x:ref>last-pos</x:ref> = 1*DIGIT
+
+<x:ref>mailbox</x:ref> = &lt;mailbox, see <xref target="RFC5322" x:fmt="," x:sec="3.4"/>&gt;
+<x:ref>media-range</x:ref> = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) )
+ parameters
+<x:ref>media-type</x:ref> = type "/" subtype parameters
+<x:ref>method</x:ref> = token
+<x:ref>minute</x:ref> = 2DIGIT
+<x:ref>month</x:ref> = %x4A.61.6E ; Jan
+ / %x46.65.62 ; Feb
+ / %x4D.61.72 ; Mar
+ / %x41.70.72 ; Apr
+ / %x4D.61.79 ; May
+ / %x4A.75.6E ; Jun
+ / %x4A.75.6C ; Jul
+ / %x41.75.67 ; Aug
+ / %x53.65.70 ; Sep
+ / %x4F.63.74 ; Oct
+ / %x4E.6F.76 ; Nov
+ / %x44.65.63 ; Dec
+
+<x:ref>obs-date</x:ref> = rfc850-date / asctime-date
+<x:ref>obs-text</x:ref> = %x80-FF
+<x:ref>opaque-tag</x:ref> = DQUOTE *etagc DQUOTE
+<x:ref>other-range</x:ref> = 1*( %x21-2B ; '!'-'+'
+ / %x2D-7E ; '-'-'~'
+ )
+
+<x:ref>parameter</x:ref> = parameter-name "=" parameter-value
+<x:ref>parameter-name</x:ref> = token
+<x:ref>parameter-value</x:ref> = ( token / quoted-string )
+<x:ref>parameters</x:ref> = *( OWS ";" OWS [ parameter ] )
+<x:ref>partial-URI</x:ref> = relative-part [ "?" query ]
+<x:ref>path-abempty</x:ref> = &lt;path-abempty, see <xref target="URI" x:fmt="," x:sec="3.3"/>&gt;
+<x:ref>port</x:ref> = &lt;port, see <xref target="URI" x:fmt="," x:sec="3.2.3"/>&gt;
+<x:ref>product</x:ref> = token [ "/" product-version ]
+<x:ref>product-version</x:ref> = token
+<x:ref>protocol</x:ref> = protocol-name [ "/" protocol-version ]
+<x:ref>protocol-name</x:ref> = token
+<x:ref>protocol-version</x:ref> = token
+<x:ref>pseudonym</x:ref> = token
+
+<x:ref>qdtext</x:ref> = HTAB / SP / "!" / %x23-5B ; '#'-'['
+ / %x5D-7E ; ']'-'~'
+ / obs-text
+<x:ref>query</x:ref> = &lt;query, see <xref target="URI" x:fmt="," x:sec="3.4"/>&gt;
+<x:ref>quoted-pair</x:ref> = "\" ( HTAB / SP / VCHAR / obs-text )
+<x:ref>quoted-string</x:ref> = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+<x:ref>qvalue</x:ref> = ( "0" [ "." *3DIGIT ] ) / ( "1" [ "." *3"0" ] )
+
+<x:ref>range-resp</x:ref> = incl-range "/" ( complete-length / "*" )
+<x:ref>range-set</x:ref> = range-spec *( OWS "," OWS range-spec )
+<x:ref>range-spec</x:ref> = int-range / suffix-range / other-range
+<x:ref>range-unit</x:ref> = token
+<x:ref>ranges-specifier</x:ref> = range-unit "=" range-set
+<x:ref>received-by</x:ref> = pseudonym [ ":" port ]
+<x:ref>received-protocol</x:ref> = [ protocol-name "/" ] protocol-version
+<x:ref>relative-part</x:ref> = &lt;relative-part, see <xref target="URI" x:fmt="," x:sec="4.2"/>&gt;
+<x:ref>rfc850-date</x:ref> = day-name-l "," SP date2 SP time-of-day SP GMT
+
+<x:ref>second</x:ref> = 2DIGIT
+<x:ref>segment</x:ref> = &lt;segment, see <xref target="URI" x:fmt="," x:sec="3.3"/>&gt;
+<x:ref>subtype</x:ref> = token
+<x:ref>suffix-length</x:ref> = 1*DIGIT
+<x:ref>suffix-range</x:ref> = "-" suffix-length
+
+<x:ref>t-codings</x:ref> = "trailers" / ( transfer-coding [ weight ] )
+<x:ref>tchar</x:ref> = "!" / "#" / "$" / "%" / "&amp;" / "'" / "*" / "+" / "-" / "." /
+ "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
+<x:ref>time-of-day</x:ref> = hour ":" minute ":" second
+<x:ref>token</x:ref> = 1*tchar
+<x:ref>token68</x:ref> = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" )
+ *"="
+<x:ref>transfer-coding</x:ref> = token *( OWS ";" OWS transfer-parameter )
+<x:ref>transfer-parameter</x:ref> = token BWS "=" BWS ( token / quoted-string )
+<x:ref>type</x:ref> = token
+
+<x:ref>unsatisfied-range</x:ref> = "*/" complete-length
+<x:ref>uri-host</x:ref> = &lt;host, see <xref target="URI" x:fmt="," x:sec="3.2.2"/>&gt;
+
+<x:ref>weak</x:ref> = %x57.2F ; W/
+<x:ref>weight</x:ref> = OWS ";" OWS "q=" qvalue
+
+<x:ref>year</x:ref> = 4DIGIT
+</sourcecode>
+</section>
+<?ENDINC build/draft-ietf-httpbis-semantics-latest.abnf-appendix ?>
+
+<section title="Changes from Previous RFCs" anchor="changes.from.previous.rfcs">
+
+<section title="Changes from RFC 2818" anchor="changes.from.rfc.2818">
+<t>
+   None.
+</t>
+</section>
+
+<section title="Changes from RFC 7230" anchor="changes.from.rfc.7230">
+<t>
+   The sections introducing HTTP's design goals, history, architecture,
+   conformance criteria, protocol versioning, URIs, message routing, and
+   header fields have been moved here.
+</t>
+<t>
+   The requirement on semantic conformance has been replaced with permission to
+   ignore or work around implementation-specific failures.
+   (<xref target="requirements.notation"/>)
+</t>
+<t>
+   The description of an origin and authoritative access to origin servers has
+   been extended for both "http" and "https" URIs to account for alternative
+   services and secured connections that are not necessarily based on TCP.
+   (Sections <xref target="http.uri" format="counter"/>, <xref target="https.uri" format="counter"/>,
+   <xref target="origin" format="counter"/>, and <xref target="routing.origin" format="counter"/>)
+</t>
+<t>
+   Explicit requirements have been added to check the target URI scheme's semantics
+   and reject requests that don't meet any associated requirements.
+   (<xref target="routing.reject"/>)
+</t>
+<t>
+   Parameters in media type, media range, and expectation can be empty via
+   one or more trailing semicolons.
+   (<xref target="parameter"/>)
+</t>
+<t>
+   "Field value" now refers to the value after multiple field lines are combined
+   with commas — by far the most common use. To refer to a single header
+   line's value, use "field line value".
+   (<xref target="header.fields"/>)
+</t>
+<t>
+   Trailer field semantics now transcend the specifics of chunked transfer coding.
+   The use of trailer fields has been further limited to allow generation
+   as a trailer field only when the sender knows the field defines that usage and
+   to allow merging into the header section only if the recipient knows the
+   corresponding field definition permits and defines how to merge. In all
+   other cases, implementations are encouraged either to store the trailer
+   fields separately or to discard them instead of merging.
+   (<xref target="trailers.limitations"/>)
+</t>
+<t>
+   The priority of the absolute form of the request URI over the Host
+   header field by origin servers has been made explicit to align with proxy handling.
+   (<xref target="field.host"/>)
+</t>
+<t>
+   The grammar definition for the Via field's "received-by" was
+   expanded in RFC 7230 due to changes in the URI grammar for host
+   <xref target="URI"/> that are not desirable for Via. For simplicity,
+   we have removed uri-host from the received-by production because it can
+   be encompassed by the existing grammar for pseudonym. In particular, this
+   change removed comma from the allowed set of characters for a host name in
+   received-by.
+   (<xref target="field.via"/>)
+</t>
+</section>
+
+<section title="Changes from RFC 7231" anchor="changes.from.rfc.7231">
+<t>
+   Minimum URI lengths to be supported by implementations are now recommended.
+   (<xref target="uri.references"/>)
+</t>
+<t>
+   The following have been clarified: CR and NUL in field values are to be
+   rejected or mapped to SP, and leading and trailing whitespace needs to be
+   stripped from field values before they are consumed.
+   (<xref target="fields.values"/>)
+</t>
+<t>
+   Parameters in media type, media range, and expectation can be empty via
+   one or more trailing semicolons.
+   (<xref target="parameter"/>)
+</t>
+<t>
+   An abstract data type for HTTP messages has been introduced to define the
+   components of a message and their semantics as an abstraction across
+   multiple HTTP versions, rather than in terms of the specific syntax form of
+   HTTP/1.1 in <xref target="HTTP11"/>, and reflect the contents after the
+   message is parsed. This makes it easier to distinguish between requirements
+   on the content (what is conveyed) versus requirements on the messaging
+   syntax (how it is conveyed) and avoids baking limitations of early protocol
+   versions into the future of HTTP. (<xref target="message.abstraction"/>)
+</t>
+<t>
+   The terms "payload" and "payload body" have been replaced with "content", to better
+   align with its usage elsewhere (e.g., in field names) and to avoid confusion
+   with frame payloads in HTTP/2 and HTTP/3.
+   (<xref target="content"/>)
+</t>
+<t>
+   The term "effective request URI" has been replaced with "target URI".
+   (<xref target="target.resource"/>)
+</t>
+<t>
+   Restrictions on client retries have been loosened to reflect implementation
+   behavior.
+   (<xref target="idempotent.methods"/>)
+</t>
+<t>
+   The fact that request bodies on GET, HEAD, and DELETE are not interoperable
+   has been clarified.
+   (Sections <xref target="GET" format="counter"/>, <xref target="HEAD" format="counter"/>, and <xref target="DELETE" format="counter"/>)
+</t>
+<t>
+   The use of the Content-Range header field
+   (<xref target="field.content-range"/>) as a request modifier on PUT is allowed.
+   (<xref target="PUT"/>)
+</t>
+<t>
+   A superfluous requirement about setting <x:ref>Content-Length</x:ref>
+   has been removed from the description of the OPTIONS method.
+   (<xref target="OPTIONS"/>)
+</t>
+<t>
+   The normative requirement to use the "message/http" media type in
+   TRACE responses has been removed.
+   (<xref target="TRACE"/>)
+</t>
+<t>
+   List-based grammar for <x:ref>Expect</x:ref> has been restored for
+   compatibility with RFC 2616.
+   (<xref target="field.expect"/>)
+</t>
+<t>
+   <x:ref>Accept</x:ref> and <x:ref>Accept-Encoding</x:ref> are allowed in response
+   messages; the latter was introduced by <xref target="RFC7694"/>.
+   (<xref target="request.content.negotiation"/>)
+</t>
+<t>
+   "Accept Parameters" (accept-params and accept-ext ABNF production) have
+   been removed from the definition of the Accept field.
+   (<xref target="field.accept"/>)
+</t>
+<t>
+   The Accept-Charset field is now deprecated.
+   (<xref target="field.accept-charset"/>)
+</t>
+<t>
+   The semantics of "*" in the <x:ref>Vary</x:ref> header field when other
+   values are present was clarified.
+   (<xref target="field.vary"/>)
+</t>
+<t>
+   Range units are compared in a case-insensitive fashion.
+   (<xref target="range.units"/>)
+</t>
+<t>
+   The use of the Accept-Ranges field is not restricted to origin servers.
+   (<xref target="field.accept-ranges"/>)
+</t>
+<t>
+   The process of creating a redirected request has been clarified.
+   (<xref target="status.3xx"/>)
+</t>
+<t>
+   Status code 308 (previously defined in <xref target="RFC7538"/>) has been
+   added so that it's defined closer to status codes 301, 302, and 307.
+   (<xref target="status.308"/>)
+</t>
+<t>
+   Status code 421 (previously defined in
+   <xref target="RFC7540" section="9.1.2"/>) has been added because of its general
+   applicability. 421 is no longer defined as heuristically cacheable since
+   the response is specific to the connection (not the target resource).
+   (<xref target="status.421"/>)
+</t>
+<t>
+   Status code 422 (previously defined in
+   <xref target="WEBDAV" x:fmt="of" x:sec="11.2"/>) has been added 
+   because of its general applicability.
+   (<xref target="status.422"/>)
+</t>
+</section>
+
+<section title="Changes from RFC 7232" anchor="changes.from.rfc.7232">
+<t>
+   Previous revisions of HTTP imposed an arbitrary 60-second limit on the
+   determination of whether Last-Modified was a strong validator to guard
+   against the possibility that the Date and Last-Modified values are
+   generated from different clocks or at somewhat different times during the
+   preparation of the response. This specification has relaxed that to allow
+   reasonable discretion.
+   (<xref target="lastmod.comparison"/>)
+</t>
+<t>
+   An edge-case requirement on If-Match and If-Unmodified-Since
+   has been removed that required a validator not to be sent in a 2xx
+   response if validation fails because the change request has already
+   been applied.
+   (Sections <xref target="field.if-match" format="counter"/> and
+   <xref target="field.if-unmodified-since" format="counter"/>)
+</t>
+<t>
+   The fact that If-Unmodified-Since does not apply to a resource
+   without a concept of modification time has been clarified.
+   (<xref target="field.if-unmodified-since"/>)
+</t>
+<t>
+   Preconditions can now be evaluated before the request content is processed
+   rather than waiting until the response would otherwise be successful.
+   (<xref target="evaluation"/>)
+</t>
+</section>
+
+<section title="Changes from RFC 7233" anchor="changes.from.rfc.7233">
+<t>
+   Refactored the range-unit and ranges-specifier grammars to simplify
+   and reduce artificial distinctions between bytes and other
+   (extension) range units, removing the overlapping grammar of
+   other-range-unit by defining range units generically as a token and
+   placing extensions within the scope of a range-spec (other-range).
+   This disambiguates the role of list syntax (commas) in all range sets,
+   including extension range units, for indicating a range-set of more than
+   one range. Moving the extension grammar into range specifiers also allows
+   protocol specific to byte ranges to be specified separately.
+</t>
+<t>
+   It is now possible to define Range handling on extension methods.
+   (<xref target="field.range"/>)
+</t>
+<t>
+   Described use of the <x:ref>Content-Range</x:ref> header field
+   (<xref target="field.content-range"/>) as a request modifier to perform a
+   partial PUT.
+   (<xref target="partial.PUT"/>)
+</t>
+</section>
+
+<section title="Changes from RFC 7235" anchor="changes.from.rfc.7235">
+<t>
+   None.
+</t>
+</section>
+
+<section title="Changes from RFC 7538" anchor="changes.from.rfc.7538">
+<t>
+   None.
+</t>
+</section>
+
+<section title="Changes from RFC 7615" anchor="changes.from.rfc.7615">
+<t>
+   None.
+</t>
+</section>
+
+<section title="Changes from RFC 7694" anchor="changes.from.rfc.7694">
+<t>
+   This specification includes the extension defined in <xref target="RFC7694"/>
+   but leaves out examples and deployment considerations.
+</t>
+</section>
+</section>
+
+<section title="Acknowledgements" anchor="acks" numbered="false">
+<t>
+   Aside from the current editors, the following individuals deserve special
+   recognition for their contributions to early aspects of HTTP and its
+   core specifications:
+   <contact fullname="Marc Andreessen"/>, <contact fullname="Tim Berners-Lee"/>,
+   <contact fullname="Robert Cailliau"/>, <contact fullname="Daniel W. Connolly"/>,
+   <contact fullname="Bob Denny"/>, <contact fullname="John Franks"/>,
+   <contact fullname="Jim Gettys"/>, <contact fullname="Jean-François Groff"/>,
+   <contact fullname="Phillip M. Hallam-Baker"/>, <contact fullname="Koen Holtman"/>,
+   <contact fullname="Jeffery L. Hostetler"/>, <contact fullname="Shel Kaphan"/>,
+   <contact fullname="Dave Kristol"/>, <contact fullname="Yves Lafon"/>,
+   <contact fullname="Scott D. Lawrence"/>, <contact fullname="Paul J. Leach"/>,
+   <contact fullname="Håkon W. Lie"/>, <contact fullname="Ari Luotonen"/>,
+   <contact fullname="Larry Masinter"/>, <contact fullname="Rob McCool"/>,
+   <contact fullname="Jeffrey C. Mogul"/>, <contact fullname="Lou Montulli"/>,
+   <contact fullname="David Morris"/>, <contact fullname="Henrik Frystyk Nielsen"/>,
+   <contact fullname="Dave Raggett"/>, <contact fullname="Eric Rescorla"/>,
+   <contact fullname="Tony Sanders"/>, <contact fullname="Lawrence C. Stewart"/>,
+   <contact fullname="Marc VanHeyningen"/>, and <contact fullname="Steve Zilles"/>.
+</t>
+<t>
+   This document builds on the many contributions
+   that went into past specifications of HTTP, including
+   <xref target="HTTP10"/>,
+   <xref target="RFC2068"/>,
+   <xref target="RFC2145"/>,
+   <xref target="RFC2616"/>,
+   <xref target="RFC2617"/>,
+   <xref target="RFC2818"/>,
+   <xref target="RFC7230"/>,
+   <xref target="RFC7231"/>,
+   <xref target="RFC7232"/>,
+   <xref target="RFC7233"/>,
+   <xref target="RFC7234"/>, and
+   <xref target="RFC7235"/>.
+   The acknowledgements within those documents still apply.
+</t>
+<t>
+   Since 2014, the following contributors have helped improve this
+   specification by reporting bugs, asking smart questions, drafting or
+   reviewing text, and evaluating issues:
+</t>
+<t>
+   <contact fullname="Alan Egerton"/>,
+   <contact fullname="Alex Rousskov"/>,
+   <contact fullname="Amichai Rothman"/>,
+   <contact fullname="Amos Jeffries"/>,
+   <contact fullname="Anders Kaseorg"/>,
+   <contact fullname="Andreas Gebhardt"/>,
+   <contact fullname="Anne van Kesteren"/>,
+   <contact fullname="Armin Abfalterer"/>,
+   <contact fullname="Aron Duby"/>,
+   <contact fullname="Asanka Herath"/>,
+   <contact fullname="Asbjørn Ulsberg"/>,
+   <contact fullname="Asta Olofsson"/>,
+   <contact fullname="Attila Gulyas"/>,
+   <contact fullname="Austin Wright"/>,
+   <contact fullname="Barry Pollard"/>,
+   <contact fullname="Ben Burkert"/>,
+   <contact fullname="Benjamin Kaduk"/>,
+   <contact fullname="Björn Höhrmann"/>,
+   <contact fullname="Brad Fitzpatrick"/>,
+   <contact fullname="Chris Pacejo"/>,
+   <contact fullname="Colin Bendell"/>,
+   <contact fullname="Cory Benfield"/>,
+   <contact fullname="Cory Nelson"/>,
+   <contact fullname="Daisuke Miyakawa"/>,
+   <contact fullname="Dale Worley"/>,
+   <contact fullname="Daniel Stenberg"/>,
+   <contact fullname="Danil Suits"/>,
+   <contact fullname="David Benjamin"/>,
+   <contact fullname="David Matson"/>,
+   <contact fullname="David Schinazi"/>,
+   <contact fullname="Дилян Палаузов" asciiFullname="Dilyan Palauzov"/>,
+   <contact fullname="Eric Anderson"/>,
+   <contact fullname="Eric Rescorla"/>,
+   <contact fullname="Éric Vyncke"/>,
+   <contact fullname="Erik Kline"/>,
+   <contact fullname="Erwin Pe"/>,
+   <contact fullname="Etan Kissling"/>,
+   <contact fullname="Evert Pot"/>,
+   <contact fullname="Evgeny Vrublevsky"/>,
+   <contact fullname="Florian Best"/>,
+   <contact fullname="Francesca Palombini"/>,
+   <contact fullname="Igor Lubashev"/>,
+   <contact fullname="James Callahan"/>,
+   <contact fullname="James Peach"/>,
+   <contact fullname="Jeffrey Yasskin"/>,
+   <contact fullname="Kalin Gyokov"/>,
+   <contact fullname="Kannan Goundan"/>,
+   <contact fullname="奥 一穂" asciiFullname="Kazuho Oku"/>,
+   <contact fullname="Ken Murchison"/>,
+   <contact fullname="Krzysztof Maczyński"/>,
+   <contact fullname="Lars Eggert"/>,
+   <contact fullname="Lucas Pardue"/>,
+   <contact fullname="Martin Duke"/>,
+   <contact fullname="Martin Dürst"/>,
+   <contact fullname="Martin Thomson"/>,
+   <contact fullname="Martynas Jusevičius"/>,
+   <contact fullname="Matt Menke"/>,
+   <contact fullname="Matthias Pigulla"/>,
+   <contact fullname="Mattias Grenfeldt"/>,
+   <contact fullname="Michael Osipov"/>,
+   <contact fullname="Mike Bishop"/>,
+   <contact fullname="Mike Pennisi"/>,
+   <contact fullname="Mike Taylor"/>,
+   <contact fullname="Mike West"/>,
+   <contact fullname="Mohit Sethi"/>,
+   <contact fullname="Murray Kucherawy"/>,
+   <contact fullname="Nathaniel J. Smith"/>,
+   <contact fullname="Nicholas Hurley"/>,
+   <contact fullname="Nikita Prokhorov"/>,
+   <contact fullname="Patrick McManus"/>,
+   <contact fullname="Piotr Sikora"/>,
+   <contact fullname="Poul-Henning Kamp"/>,
+   <contact fullname="Rick van Rein"/>,
+   <contact fullname="Robert Wilton"/>,
+   <contact fullname="Roberto Polli"/>,
+   <contact fullname="Roman Danyliw"/>,
+   <contact fullname="Samuel Williams"/>,
+   <contact fullname="Semyon Kholodnov"/>,
+   <contact fullname="Simon Pieters"/>,
+   <contact fullname="Simon Schüppel"/>,
+   <contact fullname="Stefan Eissing"/>,
+   <contact fullname="Taylor Hunt"/>,
+   <contact fullname="Todd Greer"/>,
+   <contact fullname="Tommy Pauly"/>,
+   <contact fullname="Vasiliy Faronov"/>,
+   <contact fullname="Vladimir Lashchev"/>,
+   <contact fullname="Wenbo Zhu"/>,
+   <contact fullname="William A. Rowe Jr."/>,
+   <contact fullname="Willy Tarreau"/>,
+   <contact fullname="Xingwei Liu"/>,
+   <contact fullname="Yishuai Li"/>, and
+   <contact fullname="Zaheduzzaman Sarker"/>.
+</t>
+</section>
+</back>
+</rfc>
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/rfc9111.html b/test/fixtures/cache-tests/spec/rfc9111.html
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/rfc9111.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html
+  SYSTEM "about:legacy-compat">
+<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>HTTP Caching</title><script type="application/javascript">
+function anchorRewrite() {
+  map = { };
+  if (window.location.hash.length >= 1) {
+    var fragid = window.location.hash.substr(1);
+    if (fragid) {
+      if (! document.getElementById(fragid)) {
+        var prefix = "rfc.";
+        var mapped = map[fragid];
+        if (mapped) {
+          window.location.hash = mapped;
+        } else if (fragid.indexOf("section-") == 0) {
+          window.location.hash = prefix + "section." + fragid.substring(8);
+        } else if (fragid.indexOf("appendix-") == 0) {
+          window.location.hash = prefix + "section." + fragid.substring(9);
+        } else if (fragid.indexOf("s-") == 0) {
+          var postfix = fragid.substring(2);
+          if (postfix.startsWith("abstract")) {
+            window.location.hash = prefix + postfix;
+          } else if (postfix.startsWith("note-")) {
+            window.location.hash = prefix + "note." + postfix.substring(5);
+          } else {
+            window.location.hash = prefix + "section." + postfix;
+          }
+        } else if (fragid.indexOf("p-") == 0) {
+          var r = fragid.substring(2);
+          var p = r.indexOf("-");
+          if (p >= 0) {
+            window.location.hash = prefix + "section." + r.substring(0, p) + ".p." + r.substring(p + 1);
+          }
+        }
+      }
+    }  
+  }
+}
+window.addEventListener('hashchange', anchorRewrite);
+window.addEventListener('DOMContentLoaded', anchorRewrite);
+</script><meta name="viewport" content="width=device-width, initial-scale=1"><meta property="og:type" content="article"><meta property="og:title" content="HTTP Caching"><meta property="og:description" content="The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages. This document obsoletes RFC 7234."><meta property="og:url" content="https://httpwg.org/specs/rfc9111.html"><link rel="stylesheet" type="text/css" href="/spec/bootstrap.min.css"><style type="text/css">
+      body {
+        padding-top: 80px;
+        padding-bottom: 80px;
+        position: relative;
+      }
+      .table.header th, .table.header td {
+        border-top: none;
+        padding: 0;
+      }
+      #sidebar {
+        margin-top: -10px;
+        height: 90%;
+        overflow-y: auto;
+        font-size: 90%;
+      }
+      #rfc\.meta {
+        width: 40%;
+        float: right
+      }
+      .toc ul {
+        list-style: none;
+      }
+      .filename {
+        color: rgb(119, 119, 119);
+        font-size: 23px;
+        font-weight: normal;
+        height: auto;
+        line-height: 23px;
+      }
+      dl {
+        margin-left: 1em;
+      }
+      dl.dl-horizontal: {
+        margin-left: 0;
+      }
+      dl > dt {
+        float: left;
+        margin-right: 1em;
+      }
+      dl.nohang > dt {
+        float: none;
+      }
+      dl > dd {
+        margin-bottom: .5em;
+      }
+      dl.compact > dd {
+        margin-bottom: 0em;
+      }
+      dl > dd > dl {
+        margin-top: 0.5em;
+        margin-bottom: 0em;
+      }
+      ul.empty {
+        list-style-type: none;
+      }
+      ul.empty li {
+        margin-top: .5em;
+      }
+      td.reference {
+        padding-right: 1em;
+        vertical-align: top;
+      }
+      .feedback {
+        position: fixed;
+        bottom: 5px;
+        right: 5px;
+      }
+      .fbbutton {
+        margin-left: 5px;
+      }
+      h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+        color: rgb(51, 51, 51);
+      }
+      span.tt {
+        font: 11pt consolas, monospace;
+        font-size-adjust: none;
+      }
+      div.banner {
+        background-color: #fee;
+        border: 2px solid #633;
+        padding: 8px 12px;
+        margin-bottom: 10px;
+      }
+      div.banner p {
+        font-size: 1.2em;
+      }
+    </style><link rel="stylesheet" type="text/css" href="/spec/style.css"><link rel="Contents" href="#rfc.toc"><link rel="Author" href="#rfc.authors"><link rel="Copyright" href="#rfc.copyrightnotice"><link rel="Index" href="#rfc.index"><link rel="Chapter" title="1 Introduction" href="#rfc.section.1"><link rel="Chapter" title="2 Overview of Cache Operation" href="#rfc.section.2"><link rel="Chapter" title="3 Storing Responses in Caches" href="#rfc.section.3"><link rel="Chapter" title="4 Constructing Responses from Caches" href="#rfc.section.4"><link rel="Chapter" title="5 Field Definitions" href="#rfc.section.5"><link rel="Chapter" title="6 Relationship to Applications and Other Caches" href="#rfc.section.6"><link rel="Chapter" title="7 Security Considerations" href="#rfc.section.7"><link rel="Chapter" title="8 IANA Considerations" href="#rfc.section.8"><link rel="Chapter" href="#rfc.section.9" title="9 References"><link rel="Appendix" title="A Collected ABNF" href="#rfc.section.A"><link rel="Appendix" title="B Changes from RFC 7234" href="#rfc.section.B"><link rel="Appendix" title="C Change Log" href="#rfc.section.C"><link rel="Appendix" title="Acknowledgments" href="#rfc.section.unnumbered-1"><meta name="generator" content="https://github.com/mnot/RFCBootstrap XSLT vendor: Saxonica http://www.saxonica.com/"><meta name="keywords" content="Hypertext Transfer Protocol, HTTP, HTTP Caching"><link rel="schema.dcterms" href="http://purl.org/dc/terms/"><meta name="dcterms.creator" content="Fielding, R."><meta name="dcterms.creator" content="Nottingham, M."><meta name="dcterms.creator" content="Reschke, J."><meta name="dcterms.identifier" content="urn:ietf:id:draft-ietf-httpbis-cache-latest"><meta name="dcterms.issued" content="2022-07-28"><meta name="dct.replaces" content="urn:ietf:rfc:7234"><meta name="dcterms.abstract" content="The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages. This document obsoletes RFC 7234."><meta name="description" content="The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages. This document obsoletes RFC 7234."></head><body><div class="container" id="top"><div class="row"><div class="col-lg-4 order-last d-none d-lg-block" id="sidebar" role="navigation"><div class="navbar"><div class="navbar-brand"><a href="#top"></a></div><br clear="all"><div class=""><div class="toc "><ul><li><a href="#rfc.section.1">1.</a>&nbsp;&nbsp;&nbsp;<a href="#caching">Introduction</a><ul><li><a href="#rfc.section.1.1">1.1.</a>&nbsp;&nbsp;&nbsp;<a href="#requirements.notation">Requirements Notation</a></li><li><a href="#rfc.section.1.2">1.2.</a>&nbsp;&nbsp;&nbsp;<a href="#notation">Syntax Notation</a></li><li><a href="#rfc.section.1.3">1.3.</a>&nbsp;&nbsp;&nbsp;<a href="#delta-seconds">Delta Seconds</a></li></ul></li><li><a href="#rfc.section.2">2.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.overview">Overview of Cache Operation</a></li><li><a href="#rfc.section.3">3.</a>&nbsp;&nbsp;&nbsp;<a href="#response.cacheability">Storing Responses in Caches</a><ul><li><a href="#rfc.section.3.1">3.1.</a>&nbsp;&nbsp;&nbsp;<a href="#storing.fields">Storing Header and Trailer Fields</a></li><li><a href="#rfc.section.3.2">3.2.</a>&nbsp;&nbsp;&nbsp;<a href="#update">Updating Stored Header Fields</a></li><li><a href="#rfc.section.3.3">3.3.</a>&nbsp;&nbsp;&nbsp;<a href="#incomplete.responses">Storing Incomplete Responses</a></li><li><a href="#rfc.section.3.4">3.4.</a>&nbsp;&nbsp;&nbsp;<a href="#combining.responses">Combining Partial Content</a></li><li><a href="#rfc.section.3.5">3.5.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.authenticated.responses">Storing Responses to Authenticated Requests</a></li></ul></li><li><a href="#rfc.section.4">4.</a>&nbsp;&nbsp;&nbsp;<a href="#constructing.responses.from.caches">Constructing Responses from Caches</a><ul><li><a href="#rfc.section.4.1">4.1.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.negotiated.responses">Calculating Cache Keys with Vary</a></li><li><a href="#rfc.section.4.2">4.2.</a>&nbsp;&nbsp;&nbsp;<a href="#expiration.model">Freshness</a><ul><li><a href="#rfc.section.4.2.1">4.2.1.</a>&nbsp;&nbsp;&nbsp;<a href="#calculating.freshness.lifetime">Calculating Freshness Lifetime</a></li><li><a href="#rfc.section.4.2.2">4.2.2.</a>&nbsp;&nbsp;&nbsp;<a href="#heuristic.freshness">Calculating Heuristic Freshness</a></li><li><a href="#rfc.section.4.2.3">4.2.3.</a>&nbsp;&nbsp;&nbsp;<a href="#age.calculations">Calculating Age</a></li><li><a href="#rfc.section.4.2.4">4.2.4.</a>&nbsp;&nbsp;&nbsp;<a href="#serving.stale.responses">Serving Stale Responses</a></li></ul></li><li><a href="#rfc.section.4.3">4.3.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.model">Validation</a><ul><li><a href="#rfc.section.4.3.1">4.3.1.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.sent">Sending a Validation Request</a></li><li><a href="#rfc.section.4.3.2">4.3.2.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.received">Handling a Received Validation Request</a></li><li><a href="#rfc.section.4.3.3">4.3.3.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.response">Handling a Validation Response</a></li><li><a href="#rfc.section.4.3.4">4.3.4.</a>&nbsp;&nbsp;&nbsp;<a href="#freshening.responses">Freshening Stored Responses upon Validation</a></li><li><a href="#rfc.section.4.3.5">4.3.5.</a>&nbsp;&nbsp;&nbsp;<a href="#head.effects">Freshening Responses with HEAD</a></li></ul></li><li><a href="#rfc.section.4.4">4.4.</a>&nbsp;&nbsp;&nbsp;<a href="#invalidation">Invalidating Stored Responses</a></li></ul></li><li><a href="#rfc.section.5">5.</a>&nbsp;&nbsp;&nbsp;<a href="#header.field.definitions">Field Definitions</a><ul><li><a href="#rfc.section.5.1">5.1.</a>&nbsp;&nbsp;&nbsp;<a href="#field.age">Age</a></li><li><a href="#rfc.section.5.2">5.2.</a>&nbsp;&nbsp;&nbsp;<a href="#field.cache-control">Cache-Control</a><ul><li><a href="#rfc.section.5.2.1">5.2.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive">Request Cache-Control Directives</a><ul><li><a href="#rfc.section.5.2.1.1">5.2.1.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.max-age">max-age</a></li><li><a href="#rfc.section.5.2.1.2">5.2.1.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.max-stale">max-stale</a></li><li><a href="#rfc.section.5.2.1.3">5.2.1.3.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.min-fresh">min-fresh</a></li><li><a href="#rfc.section.5.2.1.4">5.2.1.4.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.no-cache">no-cache</a></li><li><a href="#rfc.section.5.2.1.5">5.2.1.5.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.no-store">no-store</a></li><li><a href="#rfc.section.5.2.1.6">5.2.1.6.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.no-transform">no-transform</a></li><li><a href="#rfc.section.5.2.1.7">5.2.1.7.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.only-if-cached">only-if-cached</a></li></ul></li><li><a href="#rfc.section.5.2.2">5.2.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive">Response Cache-Control Directives</a><ul><li><a href="#rfc.section.5.2.2.1">5.2.2.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.max-age">max-age</a></li><li><a href="#rfc.section.5.2.2.2">5.2.2.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.must-revalidate">must-revalidate</a></li><li><a href="#rfc.section.5.2.2.3">5.2.2.3.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.must-understand">must-understand</a></li><li><a href="#rfc.section.5.2.2.4">5.2.2.4.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.no-cache">no-cache</a></li><li><a href="#rfc.section.5.2.2.5">5.2.2.5.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.no-store">no-store</a></li><li><a href="#rfc.section.5.2.2.6">5.2.2.6.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.no-transform">no-transform</a></li><li><a href="#rfc.section.5.2.2.7">5.2.2.7.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.private">private</a></li><li><a href="#rfc.section.5.2.2.8">5.2.2.8.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.proxy-revalidate">proxy-revalidate</a></li><li><a href="#rfc.section.5.2.2.9">5.2.2.9.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.public">public</a></li><li><a href="#rfc.section.5.2.2.10">5.2.2.10.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.s-maxage">s-maxage</a></li></ul></li><li><a href="#rfc.section.5.2.3">5.2.3.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.control.extensions">Cache Control Extensions</a></li><li><a href="#rfc.section.5.2.4">5.2.4.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.directive.registry">Cache Directive Registry</a></li></ul></li><li><a href="#rfc.section.5.3">5.3.</a>&nbsp;&nbsp;&nbsp;<a href="#field.expires">Expires</a></li><li><a href="#rfc.section.5.4">5.4.</a>&nbsp;&nbsp;&nbsp;<a href="#field.pragma">Pragma</a></li><li><a href="#rfc.section.5.5">5.5.</a>&nbsp;&nbsp;&nbsp;<a href="#field.warning">Warning</a></li></ul></li><li><a href="#rfc.section.6">6.</a>&nbsp;&nbsp;&nbsp;<a href="#history.lists">Relationship to Applications and Other Caches</a></li><li><a href="#rfc.section.7">7.</a>&nbsp;&nbsp;&nbsp;<a href="#security.considerations">Security Considerations</a><ul><li><a href="#rfc.section.7.1">7.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.poisoning">Cache Poisoning</a></li><li><a href="#rfc.section.7.2">7.2.</a>&nbsp;&nbsp;&nbsp;<a href="#security.timing">Timing Attacks</a></li><li><a href="#rfc.section.7.3">7.3.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.of.sensitive.information">Caching of Sensitive Information</a></li></ul></li><li><a href="#rfc.section.8">8.</a>&nbsp;&nbsp;&nbsp;<a href="#iana.considerations">IANA Considerations</a><ul><li><a href="#rfc.section.8.1">8.1.</a>&nbsp;&nbsp;&nbsp;<a href="#field.name.registration">Field Name Registration</a></li><li><a href="#rfc.section.8.2">8.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.directive.registration">Cache Directive Registration</a></li><li><a href="#rfc.section.8.3">8.3.</a>&nbsp;&nbsp;&nbsp;<a href="#warn.code.registration">Warn Code Registry</a></li></ul></li><li><a href="#rfc.section.9">9.</a>&nbsp;&nbsp;&nbsp;<a href="#rfc.references">References</a><ul><li><a href="#rfc.section.9.1">9.1.</a>&nbsp;&nbsp;&nbsp;<a href="#rfc.references.1">Normative References</a></li><li><a href="#rfc.section.9.2">9.2.</a>&nbsp;&nbsp;&nbsp;<a href="#rfc.references.2">Informative References</a></li></ul></li><li><a href="#rfc.section.A">A.</a>&nbsp;&nbsp;&nbsp;<a href="#collected.abnf">Collected ABNF</a></li><li><a href="#rfc.section.B">B.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.from.rfc.7234">Changes from RFC 7234</a></li><li><a href="#rfc.section.C">C.</a>&nbsp;&nbsp;&nbsp;<a href="#change.log">Change Log</a><ul><li><a href="#rfc.section.C.1">C.1.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.publication.as.rfc">Between RFC7234 and draft 00</a></li><li><a href="#rfc.section.C.2">C.2.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.00">Since draft-ietf-httpbis-cache-00</a></li><li><a href="#rfc.section.C.3">C.3.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.01">Since draft-ietf-httpbis-cache-01</a></li><li><a href="#rfc.section.C.4">C.4.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.02">Since draft-ietf-httpbis-cache-02</a></li><li><a href="#rfc.section.C.5">C.5.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.03">Since draft-ietf-httpbis-cache-03</a></li><li><a href="#rfc.section.C.6">C.6.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.04">Since draft-ietf-httpbis-cache-04</a></li><li><a href="#rfc.section.C.7">C.7.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.05">Since draft-ietf-httpbis-cache-05</a></li><li><a href="#rfc.section.C.8">C.8.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.06">Since draft-ietf-httpbis-cache-06</a></li><li><a href="#rfc.section.C.9">C.9.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.07">Since draft-ietf-httpbis-cache-07</a></li><li><a href="#rfc.section.C.10">C.10.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.08">Since draft-ietf-httpbis-cache-08</a></li><li><a href="#rfc.section.C.11">C.11.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.09">Since draft-ietf-httpbis-cache-09</a></li><li><a href="#rfc.section.C.12">C.12.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.10">Since draft-ietf-httpbis-cache-10</a></li><li><a href="#rfc.section.C.13">C.13.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.11">Since draft-ietf-httpbis-cache-11</a></li><li><a href="#rfc.section.C.14">C.14.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.12">Since draft-ietf-httpbis-cache-12</a></li><li><a href="#rfc.section.C.15">C.15.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.13">Since draft-ietf-httpbis-cache-13</a></li><li><a href="#rfc.section.C.16">C.16.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.14">Since draft-ietf-httpbis-cache-14</a></li></ul></li><li><a href="#acks">Acknowledgments</a></li><li><a href="#rfc.index">Index</a></li><li><a href="#rfc.authors">Authors' Addresses</a></li></ul></div></div></div></div><div class="col-lg-8 order-first main" role="main"><header><table class="table table-condensed header" id="rfc.headerblock"><tbody><tr><td class="text-left">HTTP Working Group</td><td class="text-right">R. Fielding, Editor</td></tr><tr><td class="text-left">Internet-Draft</td><td class="text-right">Adobe</td></tr><tr><td class="text-left">Obsoletes: <a href="https://tools.ietf.org/html/rfc7234">7234</a> (if approved)</td><td class="text-right">M. Nottingham, Editor</td></tr><tr><td class="text-left">Intended status: Standards Track</td><td class="text-right">Fastly</td></tr><tr><td class="text-left">Expires: January 29, 2023</td><td class="text-right">J. Reschke, Editor</td></tr><tr><td class="text-left"></td><td class="text-right">greenbytes</td></tr><tr><td class="text-left"></td><td class="text-right">July 28, 2022</td></tr></tbody></table><div id="rfc.title"><h1>HTTP Caching</h1><div class="filename">draft-ietf-httpbis-cache-latest</div></div></header><hr><h2 id="rfc.abstract"><a href="#rfc.abstract">Abstract</a></h2><div class="lead"><div id="rfc.abstract.p.1"><p>The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages.</p></div><div id="rfc.abstract.p.2"><p>This document obsoletes RFC 7234.</p></div></div><div class="banner"><p>This copy of the specification has test results interspersed throughout; click on ℹ️ to see them.</p></div><section id="rfc.note.1" class="note"><h2><a href="#rfc.note.1">Editorial Note</a></h2><div id="rfc.note.1.p.1"><p>This note is to be removed before publishing as an RFC.</p></div><div id="rfc.note.1.p.2"><p>Discussion of this draft takes place on the HTTP working group mailing list (ietf-http-wg@w3.org), which is archived at &lt;<a href="https://lists.w3.org/Archives/Public/ietf-http-wg/">https://lists.w3.org/Archives/Public/ietf-http-wg/</a>&gt;.</p></div><div id="rfc.note.1.p.3"><p>Working Group information can be found at &lt;<a href="https://httpwg.org/">https://httpwg.org/</a>&gt;; source code and issues list for this draft can be found at &lt;<a href="https://github.com/httpwg/http-core">https://github.com/httpwg/http-core</a>&gt;.</p></div><div id="rfc.note.1.p.4"><p>The changes in this draft are summarized in <a href="#changes.since.14" title="Since draft-ietf-httpbis-cache-14">Appendix&nbsp;C.16</a>.</p></div></section><section id="rfc.status"><h2><a href="#rfc.status">Status of This Memo</a></h2><div id="rfc.boilerplate.1.p.1"><p>This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.</p></div><div id="rfc.boilerplate.1.p.2"><p>Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at <a href="http://datatracker.ietf.org/drafts/current/">http://datatracker.ietf.org/drafts/current/</a>.</p></div><div id="rfc.boilerplate.1.p.3"><p>Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as “work in progress”.</p></div><div id="rfc.boilerplate.1.p.4"><p>This Internet-Draft will expire on January 29, 2023.</p></div></section><section id="rfc.copyrightnotice"><h2><a href="#rfc.copyrightnotice">Copyright Notice</a></h2><div id="rfc.boilerplate.2.p.1"><p>Copyright © 2022 IETF Trust and the persons identified as the document authors. All rights reserved.</p></div><div id="rfc.boilerplate.2.p.2"><p>This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (<a href="http://trustee.ietf.org/license-info">http://trustee.ietf.org/license-info</a>) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.</p></div><div id="rfc.boilerplate.2.p.3"><p>This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.</p></div></section><div class="toc d-lg-none"><ul><li><a href="#rfc.section.1">1.</a>&nbsp;&nbsp;&nbsp;<a href="#caching">Introduction</a><ul><li><a href="#rfc.section.1.1">1.1.</a>&nbsp;&nbsp;&nbsp;<a href="#requirements.notation">Requirements Notation</a></li><li><a href="#rfc.section.1.2">1.2.</a>&nbsp;&nbsp;&nbsp;<a href="#notation">Syntax Notation</a></li><li><a href="#rfc.section.1.3">1.3.</a>&nbsp;&nbsp;&nbsp;<a href="#delta-seconds">Delta Seconds</a></li></ul></li><li><a href="#rfc.section.2">2.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.overview">Overview of Cache Operation</a></li><li><a href="#rfc.section.3">3.</a>&nbsp;&nbsp;&nbsp;<a href="#response.cacheability">Storing Responses in Caches</a><ul><li><a href="#rfc.section.3.1">3.1.</a>&nbsp;&nbsp;&nbsp;<a href="#storing.fields">Storing Header and Trailer Fields</a></li><li><a href="#rfc.section.3.2">3.2.</a>&nbsp;&nbsp;&nbsp;<a href="#update">Updating Stored Header Fields</a></li><li><a href="#rfc.section.3.3">3.3.</a>&nbsp;&nbsp;&nbsp;<a href="#incomplete.responses">Storing Incomplete Responses</a></li><li><a href="#rfc.section.3.4">3.4.</a>&nbsp;&nbsp;&nbsp;<a href="#combining.responses">Combining Partial Content</a></li><li><a href="#rfc.section.3.5">3.5.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.authenticated.responses">Storing Responses to Authenticated Requests</a></li></ul></li><li><a href="#rfc.section.4">4.</a>&nbsp;&nbsp;&nbsp;<a href="#constructing.responses.from.caches">Constructing Responses from Caches</a><ul><li><a href="#rfc.section.4.1">4.1.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.negotiated.responses">Calculating Cache Keys with Vary</a></li><li><a href="#rfc.section.4.2">4.2.</a>&nbsp;&nbsp;&nbsp;<a href="#expiration.model">Freshness</a><ul><li><a href="#rfc.section.4.2.1">4.2.1.</a>&nbsp;&nbsp;&nbsp;<a href="#calculating.freshness.lifetime">Calculating Freshness Lifetime</a></li><li><a href="#rfc.section.4.2.2">4.2.2.</a>&nbsp;&nbsp;&nbsp;<a href="#heuristic.freshness">Calculating Heuristic Freshness</a></li><li><a href="#rfc.section.4.2.3">4.2.3.</a>&nbsp;&nbsp;&nbsp;<a href="#age.calculations">Calculating Age</a></li><li><a href="#rfc.section.4.2.4">4.2.4.</a>&nbsp;&nbsp;&nbsp;<a href="#serving.stale.responses">Serving Stale Responses</a></li></ul></li><li><a href="#rfc.section.4.3">4.3.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.model">Validation</a><ul><li><a href="#rfc.section.4.3.1">4.3.1.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.sent">Sending a Validation Request</a></li><li><a href="#rfc.section.4.3.2">4.3.2.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.received">Handling a Received Validation Request</a></li><li><a href="#rfc.section.4.3.3">4.3.3.</a>&nbsp;&nbsp;&nbsp;<a href="#validation.response">Handling a Validation Response</a></li><li><a href="#rfc.section.4.3.4">4.3.4.</a>&nbsp;&nbsp;&nbsp;<a href="#freshening.responses">Freshening Stored Responses upon Validation</a></li><li><a href="#rfc.section.4.3.5">4.3.5.</a>&nbsp;&nbsp;&nbsp;<a href="#head.effects">Freshening Responses with HEAD</a></li></ul></li><li><a href="#rfc.section.4.4">4.4.</a>&nbsp;&nbsp;&nbsp;<a href="#invalidation">Invalidating Stored Responses</a></li></ul></li><li><a href="#rfc.section.5">5.</a>&nbsp;&nbsp;&nbsp;<a href="#header.field.definitions">Field Definitions</a><ul><li><a href="#rfc.section.5.1">5.1.</a>&nbsp;&nbsp;&nbsp;<a href="#field.age">Age</a></li><li><a href="#rfc.section.5.2">5.2.</a>&nbsp;&nbsp;&nbsp;<a href="#field.cache-control">Cache-Control</a><ul><li><a href="#rfc.section.5.2.1">5.2.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive">Request Cache-Control Directives</a><ul><li><a href="#rfc.section.5.2.1.1">5.2.1.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.max-age">max-age</a></li><li><a href="#rfc.section.5.2.1.2">5.2.1.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.max-stale">max-stale</a></li><li><a href="#rfc.section.5.2.1.3">5.2.1.3.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.min-fresh">min-fresh</a></li><li><a href="#rfc.section.5.2.1.4">5.2.1.4.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.no-cache">no-cache</a></li><li><a href="#rfc.section.5.2.1.5">5.2.1.5.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.no-store">no-store</a></li><li><a href="#rfc.section.5.2.1.6">5.2.1.6.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.no-transform">no-transform</a></li><li><a href="#rfc.section.5.2.1.7">5.2.1.7.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-request-directive.only-if-cached">only-if-cached</a></li></ul></li><li><a href="#rfc.section.5.2.2">5.2.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive">Response Cache-Control Directives</a><ul><li><a href="#rfc.section.5.2.2.1">5.2.2.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.max-age">max-age</a></li><li><a href="#rfc.section.5.2.2.2">5.2.2.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.must-revalidate">must-revalidate</a></li><li><a href="#rfc.section.5.2.2.3">5.2.2.3.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.must-understand">must-understand</a></li><li><a href="#rfc.section.5.2.2.4">5.2.2.4.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.no-cache">no-cache</a></li><li><a href="#rfc.section.5.2.2.5">5.2.2.5.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.no-store">no-store</a></li><li><a href="#rfc.section.5.2.2.6">5.2.2.6.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.no-transform">no-transform</a></li><li><a href="#rfc.section.5.2.2.7">5.2.2.7.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.private">private</a></li><li><a href="#rfc.section.5.2.2.8">5.2.2.8.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.proxy-revalidate">proxy-revalidate</a></li><li><a href="#rfc.section.5.2.2.9">5.2.2.9.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.public">public</a></li><li><a href="#rfc.section.5.2.2.10">5.2.2.10.</a>&nbsp;&nbsp;&nbsp;<a href="#cache-response-directive.s-maxage">s-maxage</a></li></ul></li><li><a href="#rfc.section.5.2.3">5.2.3.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.control.extensions">Cache Control Extensions</a></li><li><a href="#rfc.section.5.2.4">5.2.4.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.directive.registry">Cache Directive Registry</a></li></ul></li><li><a href="#rfc.section.5.3">5.3.</a>&nbsp;&nbsp;&nbsp;<a href="#field.expires">Expires</a></li><li><a href="#rfc.section.5.4">5.4.</a>&nbsp;&nbsp;&nbsp;<a href="#field.pragma">Pragma</a></li><li><a href="#rfc.section.5.5">5.5.</a>&nbsp;&nbsp;&nbsp;<a href="#field.warning">Warning</a></li></ul></li><li><a href="#rfc.section.6">6.</a>&nbsp;&nbsp;&nbsp;<a href="#history.lists">Relationship to Applications and Other Caches</a></li><li><a href="#rfc.section.7">7.</a>&nbsp;&nbsp;&nbsp;<a href="#security.considerations">Security Considerations</a><ul><li><a href="#rfc.section.7.1">7.1.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.poisoning">Cache Poisoning</a></li><li><a href="#rfc.section.7.2">7.2.</a>&nbsp;&nbsp;&nbsp;<a href="#security.timing">Timing Attacks</a></li><li><a href="#rfc.section.7.3">7.3.</a>&nbsp;&nbsp;&nbsp;<a href="#caching.of.sensitive.information">Caching of Sensitive Information</a></li></ul></li><li><a href="#rfc.section.8">8.</a>&nbsp;&nbsp;&nbsp;<a href="#iana.considerations">IANA Considerations</a><ul><li><a href="#rfc.section.8.1">8.1.</a>&nbsp;&nbsp;&nbsp;<a href="#field.name.registration">Field Name Registration</a></li><li><a href="#rfc.section.8.2">8.2.</a>&nbsp;&nbsp;&nbsp;<a href="#cache.directive.registration">Cache Directive Registration</a></li><li><a href="#rfc.section.8.3">8.3.</a>&nbsp;&nbsp;&nbsp;<a href="#warn.code.registration">Warn Code Registry</a></li></ul></li><li><a href="#rfc.section.9">9.</a>&nbsp;&nbsp;&nbsp;<a href="#rfc.references">References</a><ul><li><a href="#rfc.section.9.1">9.1.</a>&nbsp;&nbsp;&nbsp;<a href="#rfc.references.1">Normative References</a></li><li><a href="#rfc.section.9.2">9.2.</a>&nbsp;&nbsp;&nbsp;<a href="#rfc.references.2">Informative References</a></li></ul></li><li><a href="#rfc.section.A">A.</a>&nbsp;&nbsp;&nbsp;<a href="#collected.abnf">Collected ABNF</a></li><li><a href="#rfc.section.B">B.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.from.rfc.7234">Changes from RFC 7234</a></li><li><a href="#rfc.section.C">C.</a>&nbsp;&nbsp;&nbsp;<a href="#change.log">Change Log</a><ul><li><a href="#rfc.section.C.1">C.1.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.publication.as.rfc">Between RFC7234 and draft 00</a></li><li><a href="#rfc.section.C.2">C.2.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.00">Since draft-ietf-httpbis-cache-00</a></li><li><a href="#rfc.section.C.3">C.3.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.01">Since draft-ietf-httpbis-cache-01</a></li><li><a href="#rfc.section.C.4">C.4.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.02">Since draft-ietf-httpbis-cache-02</a></li><li><a href="#rfc.section.C.5">C.5.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.03">Since draft-ietf-httpbis-cache-03</a></li><li><a href="#rfc.section.C.6">C.6.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.04">Since draft-ietf-httpbis-cache-04</a></li><li><a href="#rfc.section.C.7">C.7.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.05">Since draft-ietf-httpbis-cache-05</a></li><li><a href="#rfc.section.C.8">C.8.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.06">Since draft-ietf-httpbis-cache-06</a></li><li><a href="#rfc.section.C.9">C.9.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.07">Since draft-ietf-httpbis-cache-07</a></li><li><a href="#rfc.section.C.10">C.10.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.08">Since draft-ietf-httpbis-cache-08</a></li><li><a href="#rfc.section.C.11">C.11.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.09">Since draft-ietf-httpbis-cache-09</a></li><li><a href="#rfc.section.C.12">C.12.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.10">Since draft-ietf-httpbis-cache-10</a></li><li><a href="#rfc.section.C.13">C.13.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.11">Since draft-ietf-httpbis-cache-11</a></li><li><a href="#rfc.section.C.14">C.14.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.12">Since draft-ietf-httpbis-cache-12</a></li><li><a href="#rfc.section.C.15">C.15.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.13">Since draft-ietf-httpbis-cache-13</a></li><li><a href="#rfc.section.C.16">C.16.</a>&nbsp;&nbsp;&nbsp;<a href="#changes.since.14">Since draft-ietf-httpbis-cache-14</a></li></ul></li><li><a href="#acks">Acknowledgments</a></li><li><a href="#rfc.index">Index</a></li><li><a href="#rfc.authors">Authors' Addresses</a></li></ul></div><section id="caching"><h2 id="rfc.section.1" class="np"><a href="#rfc.section.1">1.</a>&nbsp;<a href="#caching">Introduction</a></h2><div id="rfc.section.1.p.1"><p>The Hypertext Transfer Protocol (HTTP) is a stateless application-level request/response protocol that uses extensible semantics and self-descriptive messages for flexible interaction with network-based hypertext information systems. It is typically used for distributed information systems, where the use of response caches can improve performance. This document defines aspects of HTTP related to caching and reusing response messages.</p></div><div id="rfc.iref.c.1"></div><div id="rfc.section.1.p.2"><p>An HTTP cache is a local store of response messages and the subsystem that controls storage, retrieval, and deletion of messages in it. A cache stores cacheable responses to reduce the response time and network bandwidth consumption on future equivalent requests. Any client or server MAY use a cache, though not when acting as a tunnel.</p></div><div id="rfc.iref.s.1"></div><div id="rfc.iref.p.1"></div><div id="rfc.section.1.p.3"><p id="shared.and.private.caches">A shared cache is a cache that stores responses for reuse by more than one user; shared caches are usually (but not always) deployed as a part of an intermediary. A private cache, in contrast, is dedicated to a single user; often, they are deployed as a component of a user agent.</p></div><div id="rfc.section.1.p.4"><p>HTTP caching's goal is significantly improving performance by reusing a prior response message to satisfy a current request. A cache considers a stored response "fresh", as defined in <a href="#expiration.model" title="Freshness">Section&nbsp;4.2</a>, if it can be reused without "validation" (checking with the origin server to see if the cached response remains valid for this request). A fresh response can therefore reduce both latency and network overhead each time the cache reuses it. When a cached response is not fresh, it might still be reusable if validation can freshen it (<a href="#validation.model" title="Validation">Section&nbsp;4.3</a>) or if the origin is unavailable (<a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>).</p></div><div id="rfc.section.1.p.5"><p>This document obsoletes <a href="#RFC7234">RFC 7234</a>, with the changes being summarized in <a href="#changes.from.rfc.7234" title="Changes from RFC 7234">Appendix&nbsp;B</a>.</p></div><section id="requirements.notation"><h3 id="rfc.section.1.1"><a href="#rfc.section.1.1">1.1.</a>&nbsp;<a href="#requirements.notation">Requirements Notation</a></h3><div id="rfc.section.1.1.p.1"><p>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 <a href="#RFC2119"><cite title="Key words for use in RFCs to Indicate Requirement Levels">[RFC2119]</cite></a> <a href="#RFC8174"><cite title="Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words">[RFC8174]</cite></a> when, and only when, they appear in all capitals, as shown here.</p></div><div id="rfc.section.1.1.p.2"><p>Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a> defines conformance criteria and contains considerations regarding error handling.</p></div></section><section id="notation"><h3 id="rfc.section.1.2"><a href="#rfc.section.1.2">1.2.</a>&nbsp;<a href="#notation">Syntax Notation</a></h3><div id="rfc.section.1.2.p.1"><p>This specification uses the Augmented Backus-Naur Form (ABNF) notation of <a href="#RFC5234"><cite title="Augmented BNF for Syntax Specifications: ABNF">[RFC5234]</cite></a>, extended with the notation for case-sensitivity in strings defined in <a href="#RFC7405"><cite title="Case-Sensitive String Support in ABNF">[RFC7405]</cite></a>.</p></div><div id="rfc.section.1.2.p.2"><p>It also uses a list extension, defined in Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>, that allows for compact definition of comma-separated lists using a '#' operator (similar to how the '*' operator indicates repetition). <a href="#collected.abnf" title="Collected ABNF">Appendix&nbsp;A</a> shows the collected grammar with all list operators expanded to standard ABNF notation.</p></div><div id="rfc.section.1.2.p.3"><p id="core.rules">The following core rule is included by reference, as defined in <a href="#RFC5234"><cite title="Augmented BNF for Syntax Specifications: ABNF">[RFC5234]</cite></a>, Appendix B.1: DIGIT (decimal 0-9).</p></div><div id="rfc.section.1.2.p.4" class="avoidbreakafter"><p id="imported.rules"><a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a> defines the following rules:</p></div><div id="rfc.figure.u.1"><pre class="inline">
+  HTTP-date     = &lt;HTTP-date, see [Semantics], Appendix ERROR: Anchor 'http.date' in Semantics not found in source file 'rfc9112.xml'.&gt;
+  OWS           = &lt;OWS, see [Semantics], Appendix ERROR: Anchor 'whitespace' in Semantics not found in source file 'rfc9112.xml'.&gt;
+  field-name    = &lt;field-name, see [Semantics], Appendix ERROR: Anchor 'field-names' in Semantics not found in source file 'rfc9112.xml'.&gt;
+  quoted-string = &lt;quoted-string, see [Semantics], Appendix ERROR: Anchor 'quoted.strings' in Semantics not found in source file 'rfc9112.xml'.&gt;
+  token         = &lt;token, see [Semantics], Appendix ERROR: Anchor 'tokens' in Semantics not found in source file 'rfc9112.xml'.&gt;
+</pre></div></section><section id="delta-seconds"><h3 id="rfc.section.1.3"><a href="#rfc.section.1.3">1.3.</a>&nbsp;<a href="#delta-seconds">Delta Seconds</a></h3><div id="rfc.section.1.3.p.1"><p>The delta-seconds rule specifies a non-negative integer, representing time in seconds.</p></div><div id="rfc.figure.u.2"><div id="rfc.iref.g.1"></div><pre class="inline">
+  delta-seconds  = 1*DIGIT
+</pre></div><div id="rfc.section.1.3.p.2"><p>A recipient parsing a delta-seconds value and converting it to binary form ought to use an arithmetic type of at least 31 bits of non-negative integer range. If a cache receives a delta-seconds value greater than the greatest integer it can represent, or if any of its subsequent calculations overflows, the cache MUST consider the value to be 2147483648 (2^31) or the greatest positive integer it can conveniently represent.</p></div><div id="rfc.section.1.3.p.3"><ul class="empty"><li>Note: The value 2147483648 is here for historical reasons, represents infinity (over 68 years), and does not need to be stored in binary form; an implementation could produce it as a canned string if any overflow occurs, even if the calculations are performed with an arithmetic type incapable of directly representing that number. What matters here is that an overflow be detected and not treated as a negative value in later calculations.</li></ul></div></section></section><section id="caching.overview"><h2 id="rfc.section.2"><a href="#rfc.section.2">2.</a>&nbsp;<a href="#caching.overview">Overview of Cache Operation</a></h2><div id="rfc.section.2.p.1"><p>Proper cache operation preserves the semantics of HTTP transfers (<a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) while reducing the transmission of information already held in the cache. Although caching is an entirely OPTIONAL feature of HTTP, it can be assumed that reusing a cached response is desirable and that such reuse is the default behavior when no requirement or local configuration prevents it. Therefore, HTTP cache requirements are focused on preventing a cache from either storing a non-reusable response or reusing a stored response inappropriately, rather than mandating that caches always store and reuse particular responses.</p></div><div id="rfc.iref.c.2"></div><div id="rfc.section.2.p.2"><p>The cache key is the information a cache uses to select a response and is comprised of, at a minimum, the request method and target URI used to retrieve the stored response; the method determines under which circumstances that response can be used to satisfy a subsequent request. However, many HTTP caches in common use today only cache GET responses, and therefore only use the URI as the cache key, forwarding other methods.</p></div><div id="rfc.section.2.p.3"><p>If a request target is subject to content negotiation, the cache might store multiple responses for it. Caches differentiate these responses by incorporating values of the original request's selecting header fields into the cache key as well, using information in the Vary response header field, as per <a href="#caching.negotiated.responses" title="Calculating Cache Keys with Vary">Section&nbsp;4.1</a>.</p></div><div id="rfc.section.2.p.4"><p>Caches might incorporate additional material into the cache key. For example, user agent caches might include the referring site's identity, thereby "double keying" the cache to avoid some privacy risks (see <a href="#security.timing" title="Timing Attacks">Section&nbsp;7.2</a>).</p></div><div id="rfc.section.2.p.5"><p>Most commonly, caches store the successful result of a retrieval request: i.e., a 200 (OK) response to a GET request, which contains a representation of the target resource (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>). However, it is also possible to store redirects, negative results (e.g., 404 (Not Found)), incomplete results (e.g., 206 (Partial Content)), and responses to methods other than GET if the method's definition allows such caching and defines something suitable for use as a cache key.</p></div><div id="rfc.section.2.p.6"><p>A cache is disconnected when it cannot contact the origin server or otherwise find a forward path for a request. A disconnected cache can serve stale responses in some circumstances (<a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>).</p></div></section><section id="response.cacheability"><h2 id="rfc.section.3"><a href="#rfc.section.3">3.</a>&nbsp;<a href="#response.cacheability">Storing Responses in Caches</a></h2><div id="rfc.section.3.p.1" class="avoidbreakafter"><p>A cache MUST NOT store a response to a request unless:</p></div><div id="rfc.section.3.p.2"><ul><li>the request method is understood by the cache;</li><li>the response status code is final (see Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>);</li><li>if the response status code is 206 or 304, or the "must-understand" cache directive (see <a href="#cache-response-directive.must-understand" title="must-understand">Section&nbsp;5.2.2.3</a>) is present: the cache understands the response status code;</li><li>the "no-store" cache directive is not present in the response (see <a href="#cache-response-directive.no-store" title="no-store">Section&nbsp;5.2.2.5</a>);</li><li>if the cache is shared: the "private" response directive is either not present or allows a shared cache to store a modified response; see <a href="#cache-response-directive.private" title="private">Section&nbsp;5.2.2.7</a>);</li><li>if the cache is shared: the Authorization header field is not present in the request (see Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) or a response directive is present that explicitly allows shared caching (see <a href="#caching.authenticated.responses" title="Storing Responses to Authenticated Requests">Section&nbsp;3.5</a>); and,</li><li>the response contains at least one of:<br><br> <ul><li>a public response directive (see <a href="#cache-response-directive.public" title="public">Section&nbsp;5.2.2.9</a>);</li><li>a private response directive, if the cache is not shared (see <a href="#cache-response-directive.private" title="private">Section&nbsp;5.2.2.7</a>);</li><li>an <a href="#field.expires">Expires</a> header field (see <a href="#field.expires" title="Expires">Section&nbsp;5.3</a>);</li><li>a max-age response directive (see <a href="#cache-response-directive.max-age" title="max-age">Section&nbsp;5.2.2.1</a>);</li><li>if the cache is shared: an s-maxage response directive (see <a href="#cache-response-directive.s-maxage" title="s-maxage">Section&nbsp;5.2.2.10</a>);</li><li>a Cache Control Extension that allows it to be cached (see <a href="#cache.control.extensions" title="Cache Control Extensions">Section&nbsp;5.2.3</a>); or,</li><li>a status code that is defined as heuristically cacheable (see <a href="#heuristic.freshness" title="Calculating Heuristic Freshness">Section&nbsp;4.2.2</a>).</li></ul><br><br> </li></ul></div><div id="rfc.section.3.p.3"><p>Note that a cache-control extension can override any of the requirements listed; see <a href="#cache.control.extensions" title="Cache Control Extensions">Section&nbsp;5.2.3</a>.</p></div><div id="rfc.section.3.p.4"><p>In this context, a cache has "understood" a request method or a response status code if it recognizes it and implements all specified caching-related behavior.</p></div><div id="rfc.section.3.p.5"><p>Note that, in normal operation, some caches will not store a response that has neither a cache validator nor an explicit expiration time, as such responses are not usually useful to store. However, caches are not prohibited from storing such responses.</p></div><section id="storing.fields"><h3 id="rfc.section.3.1"><a href="#rfc.section.3.1">3.1.</a>&nbsp;<a href="#storing.fields">Storing Header and Trailer Fields</a></h3><div id="rfc.section.3.1.p.1" class="avoidbreakafter"><p>Caches MUST include all received response header fields — including unrecognised ones — when storing a response; this assures that new HTTP header fields can be successfully deployed. However, the following exceptions are made:</p></div><div id="rfc.section.3.1.p.2"><ul><li>The Connection header field and fields whose names are listed in it are required by Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a> to be removed before forwarding the message. This MAY be implemented by doing so before storage.</li><li>Likewise, some fields' semantics require them to be removed before forwarding the message, and this MAY be implemented by doing so before storage; see Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a> for some examples.</li><li>The no-cache (<a href="#cache-response-directive.no-cache" title="no-cache">Section&nbsp;5.2.2.4</a>) and private (<a href="#cache-response-directive.private" title="private">Section&nbsp;5.2.2.7</a>) cache directives can have arguments that prevent storage of header fields by all caches and shared caches, respectively.</li><li>Header fields that are specific to a client's proxy configuration MUST NOT be stored, unless the cache incorporates the identity of the proxy into the cache key. Effectively, this is limited to Proxy-Authenticate (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), Proxy-Authentication-Info (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), and Proxy-Authorization (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>).</li></ul></div><div id="rfc.section.3.1.p.3"><p>Caches MAY either store trailer fields separate from header fields, or discard them. Caches MUST NOT combine trailer fields with header fields.</p></div></section><section id="update"><h3 id="rfc.section.3.2"><a href="#rfc.section.3.2">3.2.</a>&nbsp;<a href="#update">Updating Stored Header Fields</a></h3><div id="rfc.section.3.2.p.1"><p>Caches are required to update a stored response's header fields from another (typically newer) response in several situations; for example, see <a href="#combining.responses" title="Combining Partial Content">Section&nbsp;3.4</a>, <a href="#freshening.responses" title="Freshening Stored Responses upon Validation">Section&nbsp;4.3.4</a> and <a href="#head.effects" title="Freshening Responses with HEAD">Section&nbsp;4.3.5</a>.</p></div><div id="rfc.section.3.2.p.2" class="avoidbreakafter"><p>When doing so, the cache MUST add each header field in the provided response to the stored response, replacing field values that are already present, with the following exceptions:</p></div><div id="rfc.section.3.2.p.3"><ul><li>Header fields excepted from storage in <a href="#storing.fields" title="Storing Header and Trailer Fields">Section&nbsp;3.1</a>,</li><li>Header fields that the cache's stored response depends upon, as described below,</li><li>Header fields that are automatically processed and removed by the recipient, as described below, and</li><li>The Content-Length header field.</li></ul></div><div id="rfc.section.3.2.p.4"><p>In some cases, caches (especially in user agents) store the results of processing the received response, rather than the response itself, and updating header fields that affect that processing can result in inconsistent behavior and security issues. Caches in this situation MAY omit these header fields from updating stored responses on an exceptional basis, but SHOULD limit such omission to those fields necessary to assure integrity of the stored response.</p></div><div id="rfc.section.3.2.p.5"><p>For example, a browser might decode the content coding of a response while it is being received, creating a disconnect between the data it has stored and the response's original metadata. Updating that stored metadata with a different Content-Encoding header field would be problematic. Likewise, a browser might store a post-parse HTML tree, rather than the content received in the response; updating the Content-Type header field would not be workable in this case, because any assumptions about the format made in parsing would now be invalid.</p></div><div id="rfc.section.3.2.p.6"><p>Furthermore, some fields are automatically processed and removed by the HTTP implementation; for example, the Content-Range header field. Implementations MAY automatically omit such header fields from updates, even when the processing does not actually occur.</p></div><div id="rfc.section.3.2.p.7"><p>Note that the Content-* prefix is not a signal that a header field is omitted from update; it is a convention for MIME header fields, not HTTP.</p></div></section><section id="incomplete.responses"><h3 id="rfc.section.3.3"><a href="#rfc.section.3.3">3.3.</a>&nbsp;<a href="#incomplete.responses">Storing Incomplete Responses</a></h3><div id="rfc.section.3.3.p.1"><p>If the request method is GET, the response status code is 200 (OK), and the entire response header section has been received, a cache MAY store a response body that is not complete (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) if the stored response is recorded as being incomplete. Likewise, a 206 (Partial Content) response MAY be stored as if it were an incomplete 200 (OK) response. However, a cache MUST NOT store incomplete or partial-content responses if it does not support the Range and Content-Range header fields or if it does not understand the range units used in those fields.</p></div><div id="rfc.section.3.3.p.2"><p>A cache MAY complete a stored incomplete response by making a subsequent range request (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) and combining the successful response with the stored response, as defined in <a href="#combining.responses" title="Combining Partial Content">Section&nbsp;3.4</a>. A cache MUST NOT use an incomplete response to answer requests unless the response has been made complete, or the request is partial and specifies a range wholly within the incomplete response. A cache MUST NOT send a partial response to a client without explicitly marking it using the 206 (Partial Content) status code.</p></div></section><section id="combining.responses"><h3 id="rfc.section.3.4"><a href="#rfc.section.3.4">3.4.</a>&nbsp;<a href="#combining.responses">Combining Partial Content</a></h3><div id="rfc.section.3.4.p.1"><p>A response might transfer only a partial representation if the connection closed prematurely or if the request used one or more Range specifiers (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>). After several such transfers, a cache might have received several ranges of the same representation. A cache MAY combine these ranges into a single stored response, and reuse that response to satisfy later requests, if they all share the same strong validator and the cache complies with the client requirements in Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div><div id="rfc.section.3.4.p.2"><p>When combining the new response with one or more stored responses, a cache MUST update the stored response header fields using the header fields provided in the new response, as per <a href="#update" title="Updating Stored Header Fields">Section&nbsp;3.2</a>.</p></div></section><section id="caching.authenticated.responses"><h3 id="rfc.section.3.5"><a href="#rfc.section.3.5">3.5.</a>&nbsp;<a href="#caching.authenticated.responses">Storing Responses to Authenticated Requests</a></h3><div id="rfc.section.3.5.p.1"><p>A shared cache MUST NOT use a cached response to a request with an Authorization header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) to satisfy any subsequent request unless the response contains a <a href="#field.cache-control">Cache-Control</a> field with a response directive (<a href="#cache-response-directive" title="Response Cache-Control Directives">Section&nbsp;5.2.2</a>) that allows it to be stored by a shared cache and the cache conforms to the requirements of that directive for that response.</p></div><div id="rfc.section.3.5.p.2"><p>In this specification, the following response directives have such an effect: must-revalidate (<a href="#cache-response-directive.must-revalidate" title="must-revalidate">Section&nbsp;5.2.2.2</a>), public (<a href="#cache-response-directive.public" title="public">Section&nbsp;5.2.2.9</a>), and s-maxage (<a href="#cache-response-directive.s-maxage" title="s-maxage">Section&nbsp;5.2.2.10</a>).</p></div></section></section><section id="constructing.responses.from.caches"><h2 id="rfc.section.4"><a href="#rfc.section.4">4.</a>&nbsp;<a href="#constructing.responses.from.caches">Constructing Responses from Caches</a></h2><div id="rfc.section.4.p.1" class="avoidbreakafter"><p>When presented with a request, a cache MUST NOT reuse a stored response, unless:</p></div><div id="rfc.section.4.p.2"><ul><li>The presented target URI (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) and that of the stored response match, and</li><li>the request method associated with the stored response allows it to be used for the presented request, and</li><li>selecting header fields nominated by the stored response (if any) match those presented (see <a href="#caching.negotiated.responses" title="Calculating Cache Keys with Vary">Section&nbsp;4.1</a>), and</li><li>the stored response does not contain the no-cache cache directive (<a href="#cache-response-directive.no-cache" title="no-cache">Section&nbsp;5.2.2.4</a>), unless it is successfully validated (<a href="#validation.model" title="Validation">Section&nbsp;4.3</a>), and</li><li>the stored response is either:<br><br> <ul><li>fresh (see <a href="#expiration.model" title="Freshness">Section&nbsp;4.2</a>), or</li><li>allowed to be served stale (see <a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>), or</li><li>successfully validated (see <a href="#validation.model" title="Validation">Section&nbsp;4.3</a>).</li></ul><br><br> </li></ul></div><div id="rfc.section.4.p.3"><p>Note that a cache-control extension can override any of the requirements listed; see <a href="#cache.control.extensions" title="Cache Control Extensions">Section&nbsp;5.2.3</a>.</p></div><div id="rfc.section.4.p.4"><p>When a stored response is used to satisfy a request without validation, a cache MUST generate an <a href="#field.age">Age</a> header field (<a href="#field.age" title="Age">Section&nbsp;5.1</a>), replacing any present in the response with a value equal to the stored response's current_age; see <a href="#age.calculations" title="Calculating Age">Section&nbsp;4.2.3</a>.</p></div><div id="rfc.section.4.p.5"><p>A cache MUST write through requests with methods that are unsafe (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) to the origin server; i.e., a cache is not allowed to generate a reply to such a request before having forwarded the request and having received a corresponding response.</p></div><div id="rfc.section.4.p.6"><p>Also, note that unsafe requests might invalidate already-stored responses; see <a href="#invalidation" title="Invalidating Stored Responses">Section&nbsp;4.4</a>.</p></div><div id="rfc.iref.c.3"></div><div id="rfc.section.4.p.7"><p>A response that is stored or storable can be used to satisfy multiple requests, provided that it is allowed to reuse that response for the requests in question. This enables caches to collapse requests — or combine multiple incoming requests into a single forward one upon a cache miss — thereby reducing load on the origin server and network. However, note that if the response returned is not able to be used for some or all of the collapsed requests, additional latency might be introduced, because they will need to be forwarded to be satisfied.</p></div><div id="rfc.section.4.p.8"><p>When more than one suitable response is stored, a cache MUST use the most recent one (as determined by the Date header field). It can also forward the request with "Cache-Control: max-age=0" or "Cache-Control: no-cache" to disambiguate which response to use.</p></div><div id="rfc.section.4.p.9"><p>A cache that does not have a clock available MUST NOT use stored responses without revalidating them upon every use.</p></div><section id="caching.negotiated.responses"><h3 id="rfc.section.4.1"><a href="#rfc.section.4.1">4.1.</a>&nbsp;<a href="#caching.negotiated.responses">Calculating Cache Keys with Vary</a></h3><div id="rfc.section.4.1.p.1"><p>When a cache receives a request that can be satisfied by a stored response that has a Vary header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), it MUST NOT use that response unless all the selecting header fields nominated by the Vary header field match in both the original request (i.e., that associated with the stored response), and the presented request.</p></div><div id="rfc.section.4.1.p.2" class="avoidbreakafter"><p>The selecting header fields from two requests are defined to match if and only if those in the first request can be transformed to those in the second request by applying any of:</p></div><div id="rfc.section.4.1.p.3"><ul><li>adding or removing whitespace, where allowed in the header field's syntax</li><li>combining multiple header field lines with the same field name (see Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>)</li><li>normalizing both header field values in a way that is known to have identical semantics, according to the header field's specification (e.g., reordering field values when order is not significant; case-normalization, where values are defined to be case-insensitive)</li></ul></div><div id="rfc.section.4.1.p.4"><p>If (after any normalization that might take place) a header field is absent from a request, it can only match another request if it is also absent there.</p></div><div id="rfc.section.4.1.p.5"><p>A Vary header field value containing a member "*" always fails to match.</p></div><div id="rfc.iref.s.2"></div><div id="rfc.section.4.1.p.6"><p>The stored response with matching selecting header fields is known as the selected response.</p></div><div id="rfc.section.4.1.p.7"><p>If multiple selected responses are available (potentially including responses without a Vary header field), the cache will need to choose one to use. When a selecting header field has a known mechanism for doing so (e.g., qvalues on Accept and similar request header fields), that mechanism MAY be used to select a preferred response. If such a mechanism is not available, or leads to equally preferred responses, the most recent response (as determined by the Date header field) is used, as per <a href="#constructing.responses.from.caches" title="Constructing Responses from Caches">Section&nbsp;4</a>.</p></div><div id="rfc.section.4.1.p.8"><p>Some resources mistakenly omit the Vary header field from their default response (i.e., the one sent when no more preferable response is available), with the effect of selecting it for requests to that resource even when more preferable responses are available. When a cache has multiple responses for a target URI and one or more omits the Vary header field, it SHOULD use the most recent (see <a href="#age.calculations" title="Calculating Age">Section&nbsp;4.2.3</a>) valid Vary field value available to select an appropriate response for the request.</p></div><div id="rfc.section.4.1.p.9"><p>If no selected response is available, the cache cannot satisfy the presented request. Typically, it is forwarded to the origin server in a (possibly conditional; see <a href="#validation.model" title="Validation">Section&nbsp;4.3</a>) request.</p></div></section><section id="expiration.model"><h3 id="rfc.section.4.2"><a href="#rfc.section.4.2">4.2.</a>&nbsp;<a href="#expiration.model">Freshness</a></h3><div id="rfc.section.4.2.p.1"><p>A fresh response is one whose age has not yet exceeded its freshness lifetime. Conversely, a stale response is one where it has.</p></div><div id="rfc.iref.f.1"></div><div id="rfc.iref.e.1"></div><div id="rfc.iref.h.1"></div><div id="rfc.section.4.2.p.2"><p>A response's freshness lifetime is the length of time between its generation by the origin server and its expiration time. An explicit expiration time is the time at which the origin server intends that a stored response can no longer be used by a cache without further validation, whereas a heuristic expiration time is assigned by a cache when no explicit expiration time is available.</p></div><div id="rfc.iref.a.1"></div><div id="rfc.section.4.2.p.3"><p>A response's age is the time that has passed since it was generated by, or successfully validated with, the origin server.</p></div><div id="rfc.section.4.2.p.4"><p>When a response is fresh, it can be used to satisfy subsequent requests without contacting the origin server, thereby improving efficiency.</p></div><div id="rfc.section.4.2.p.5"><p>The primary mechanism for determining freshness is for an origin server to provide an explicit expiration time in the future, using either the <a href="#field.expires">Expires</a> header field (<a href="#field.expires" title="Expires">Section&nbsp;5.3</a>) or the max-age response directive (<a href="#cache-response-directive.max-age" title="max-age">Section&nbsp;5.2.2.1</a>). Generally, origin servers will assign future explicit expiration times to responses in the belief that the representation is not likely to change in a semantically significant way before the expiration time is reached.</p></div><div id="rfc.section.4.2.p.6"><p>If an origin server wishes to force a cache to validate every request, it can assign an explicit expiration time in the past to indicate that the response is already stale. Compliant caches will normally validate a stale cached response before reusing it for subsequent requests (see <a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>).</p></div><div id="rfc.section.4.2.p.7"><p>Since origin servers do not always provide explicit expiration times, caches are also allowed to use a heuristic to determine an expiration time under certain circumstances (see <a href="#heuristic.freshness" title="Calculating Heuristic Freshness">Section&nbsp;4.2.2</a>).</p></div><div id="rfc.section.4.2.p.8" class="avoidbreakafter"><p>The calculation to determine if a response is fresh is:</p></div><div id="rfc.figure.u.3"><pre class="text">
+   response_is_fresh = (freshness_lifetime &gt; current_age)
+</pre></div><div id="rfc.section.4.2.p.9"><p>freshness_lifetime is defined in <a href="#calculating.freshness.lifetime" title="Calculating Freshness Lifetime">Section&nbsp;4.2.1</a>; current_age is defined in <a href="#age.calculations" title="Calculating Age">Section&nbsp;4.2.3</a>.</p></div><div id="rfc.section.4.2.p.10"><p>Clients can send the max-age or min-fresh request directives (<a href="#cache-request-directive" title="Request Cache-Control Directives">Section&nbsp;5.2.1</a>) to constrain or relax freshness calculations for the corresponding response. However, caches are not required to honor them.</p></div><div id="rfc.section.4.2.p.11" class="avoidbreakafter"><p>When calculating freshness, to avoid common problems in date parsing:</p></div><div id="rfc.section.4.2.p.12"><ul><li>Although all date formats are specified to be case-sensitive, a cache recipient SHOULD match the field value case-insensitively.</li><li>If a cache recipient's internal implementation of time has less resolution than the value of an HTTP-date, the recipient MUST internally represent a parsed <a href="#field.expires">Expires</a> date as the nearest time equal to or earlier than the received value.</li><li>A cache recipient MUST NOT allow local time zones to influence the calculation or comparison of an age or expiration time.</li><li>A cache recipient SHOULD consider a date with a zone abbreviation other than "GMT" to be invalid for calculating expiration.</li></ul></div><div id="rfc.section.4.2.p.13"><p>Note that freshness applies only to cache operation; it cannot be used to force a user agent to refresh its display or reload a resource. See <a href="#history.lists" title="Relationship to Applications and Other Caches">Section&nbsp;6</a> for an explanation of the difference between caches and history mechanisms.</p></div><section id="calculating.freshness.lifetime"><h4 id="rfc.section.4.2.1"><a href="#rfc.section.4.2.1">4.2.1.</a>&nbsp;<a href="#calculating.freshness.lifetime">Calculating Freshness Lifetime</a></h4><div id="rfc.section.4.2.1.p.1" class="avoidbreakafter"><p>A cache can calculate the freshness lifetime (denoted as freshness_lifetime) of a response by using the first match of:</p></div><div id="rfc.section.4.2.1.p.2"><ul><li>If the cache is shared and the s-maxage response directive (<a href="#cache-response-directive.s-maxage" title="s-maxage">Section&nbsp;5.2.2.10</a>) is present, use its value, or</li><li>If the max-age response directive (<a href="#cache-response-directive.max-age" title="max-age">Section&nbsp;5.2.2.1</a>) is present, use its value, or</li><li>If the <a href="#field.expires">Expires</a> response header field (<a href="#field.expires" title="Expires">Section&nbsp;5.3</a>) is present, use its value minus the value of the Date response header field (using the time the message was received if it is not present, as per Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), or</li><li>Otherwise, no explicit expiration time is present in the response. A heuristic freshness lifetime might be applicable; see <a href="#heuristic.freshness" title="Calculating Heuristic Freshness">Section&nbsp;4.2.2</a>.</li></ul></div><div id="rfc.section.4.2.1.p.3"><p>Note that this calculation is not vulnerable to clock skew, since all of the information comes from the origin server.</p></div><div id="rfc.section.4.2.1.p.4"><p>When there is more than one value present for a given directive (e.g., two <a href="#field.expires">Expires</a> header field lines or multiple Cache-Control: max-age directives), either the first occurrence should be used, or the response should be considered stale. If directives conflict (e.g., both max-age and no-cache are present), the most restrictive directive should be honored. Caches are encouraged to consider responses that have invalid freshness information (e.g., a max-age directive with non-integer content) to be stale.</p></div></section><section id="heuristic.freshness"><h4 id="rfc.section.4.2.2"><a href="#rfc.section.4.2.2">4.2.2.</a>&nbsp;<a href="#heuristic.freshness">Calculating Heuristic Freshness</a></h4><div id="rfc.section.4.2.2.p.1"><p>Since origin servers do not always provide explicit expiration times, a cache MAY assign a heuristic expiration time when an explicit time is not specified, employing algorithms that use other field values (such as the Last-Modified time) to estimate a plausible expiration time. This specification does not provide specific algorithms, but does impose worst-case constraints on their results.</p></div><div id="rfc.section.4.2.2.p.2"><p>A cache MUST NOT use heuristics to determine freshness when an explicit expiration time is present in the stored response. Because of the requirements in <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>, this means that heuristics can only be used on responses without explicit freshness whose status codes are defined as heuristically cacheable (e.g., see Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), and those responses without explicit freshness that have been marked as explicitly cacheable (e.g., with a "public" response directive).</p></div><div id="rfc.section.4.2.2.p.3"><p>Note that in previous specifications heuristically cacheable response status codes were called "cacheable by default."</p></div><div id="rfc.section.4.2.2.p.4"><p>If the response has a Last-Modified header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), caches are encouraged to use a heuristic expiration value that is no more than some fraction of the interval since that time. A typical setting of this fraction might be 10%.</p></div><div id="rfc.section.4.2.2.p.5"><ul class="empty"><li>Note: Section 13.9 of <a href="#RFC2616"><cite title="Hypertext Transfer Protocol -- HTTP/1.1">[RFC2616]</cite></a> prohibited caches from calculating heuristic freshness for URIs with query components (i.e., those containing '?'). In practice, this has not been widely implemented. Therefore, origin servers are encouraged to send explicit directives (e.g., Cache-Control: no-cache) if they wish to prevent caching.</li></ul></div></section><section id="age.calculations"><h4 id="rfc.section.4.2.3"><a href="#rfc.section.4.2.3">4.2.3.</a>&nbsp;<a href="#age.calculations">Calculating Age</a></h4><div id="rfc.section.4.2.3.p.1"><p>The <a href="#field.age">Age</a> header field is used to convey an estimated age of the response message when obtained from a cache. The Age field value is the cache's estimate of the number of seconds since the origin server generated or validated the response. The Age value is therefore the sum of the time that the response has been resident in each of the caches along the path from the origin server, plus the time it has been in transit along network paths.</p></div><div id="rfc.section.4.2.3.p.2" class="avoidbreakafter"><p>Age calculation uses the following data:</p></div><div id="rfc.section.4.2.3.p.3"><dl><dt>age_value</dt><dd>The term "age_value" denotes the value of the <a href="#field.age">Age</a> header field (<a href="#field.age" title="Age">Section&nbsp;5.1</a>), in a form appropriate for arithmetic operation; or 0, if not available.</dd><dt>date_value</dt><dd>The term "date_value" denotes the value of the Date header field, in a form appropriate for arithmetic operations. See Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a> for the definition of the Date header field, and for requirements regarding responses without it.</dd><dt>now</dt><dd>The term "now" means "the current value of the clock at the host performing the calculation". A host ought to use NTP (<a href="#RFC5905"><cite title="Network Time Protocol Version 4: Protocol and Algorithms Specification">[RFC5905]</cite></a>) or some similar protocol to synchronize its clocks to Coordinated Universal Time.</dd><dt>request_time</dt><dd>The current value of the clock at the host at the time the request resulting in the stored response was made.</dd><dt>response_time</dt><dd>The current value of the clock at the host at the time the response was received.</dd></dl></div><div id="rfc.section.4.2.3.p.4" class="avoidbreakafter"><p>A response's age can be calculated in two entirely independent ways:</p></div><div id="rfc.section.4.2.3.p.5"><ol><li>the "apparent_age": response_time minus date_value, if the local clock is reasonably well synchronized to the origin server's clock. If the result is negative, the result is replaced by zero.</li><li>the "corrected_age_value", if all of the caches along the response path implement HTTP/1.1 or greater. A cache MUST interpret this value relative to the time the request was initiated, not the time that the response was received.</li></ol></div><div id="rfc.figure.u.4"><pre class="text">
+  apparent_age = max(0, response_time - date_value);
+
+  response_delay = response_time - request_time;
+  corrected_age_value = age_value + response_delay;
+</pre></div><div id="rfc.section.4.2.3.p.6"><p>The corrected_age_value MAY be used as the corrected_initial_age. In circumstances where very old cache implementations that might not correctly insert <a href="#field.age">Age</a> are present, corrected_initial_age can be calculated more conservatively as</p></div><div id="rfc.figure.u.5"><pre class="text">
+  corrected_initial_age = max(apparent_age, corrected_age_value);
+</pre></div><div id="rfc.section.4.2.3.p.7"><p>The current_age of a stored response can then be calculated by adding the time (in seconds) since the stored response was last validated by the origin server to the corrected_initial_age.</p></div><div id="rfc.figure.u.6"><pre class="text">
+  resident_time = now - response_time;
+  current_age = corrected_initial_age + resident_time;
+</pre></div></section><section id="serving.stale.responses"><h4 id="rfc.section.4.2.4"><a href="#rfc.section.4.2.4">4.2.4.</a>&nbsp;<a href="#serving.stale.responses">Serving Stale Responses</a></h4><div id="rfc.section.4.2.4.p.1"><p>A "stale" response is one that either has explicit expiry information or is allowed to have heuristic expiry calculated, but is not fresh according to the calculations in <a href="#expiration.model" title="Freshness">Section&nbsp;4.2</a>.</p></div><div id="rfc.section.4.2.4.p.2"><p>A cache MUST NOT generate a stale response if it is prohibited by an explicit in-protocol directive (e.g., by a "no-cache" cache directive, a "must-revalidate" cache-response-directive, or an applicable "s-maxage" or "proxy-revalidate" cache-response-directive; see <a href="#cache-response-directive" title="Response Cache-Control Directives">Section&nbsp;5.2.2</a>).</p></div><div id="rfc.section.4.2.4.p.3"><p>A cache MUST NOT generate a stale response unless it is disconnected or doing so is explicitly permitted by the client or origin server (e.g., by the max-stale request directive in <a href="#cache-request-directive" title="Request Cache-Control Directives">Section&nbsp;5.2.1</a>, by extension directives such as those defined in <a href="#RFC5861"><cite title="HTTP Cache-Control Extensions for Stale Content">[RFC5861]</cite></a>, or by configuration in accordance with an out-of-band contract).</p></div></section></section><section id="validation.model"><h3 id="rfc.section.4.3"><a href="#rfc.section.4.3">4.3.</a>&nbsp;<a href="#validation.model">Validation</a></h3><div id="rfc.section.4.3.p.1"><p>When a cache has one or more stored responses for a requested URI, but cannot serve any of them (e.g., because they are not fresh, or one cannot be selected; see <a href="#caching.negotiated.responses" title="Calculating Cache Keys with Vary">Section&nbsp;4.1</a>), it can use the conditional request mechanism (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) in the forwarded request to give the next inbound server an opportunity to select a valid stored response to use, updating the stored metadata in the process, or to replace the stored response(s) with a new response. This process is known as validating or revalidating the stored response.</p></div><section id="validation.sent"><h4 id="rfc.section.4.3.1"><a href="#rfc.section.4.3.1">4.3.1.</a>&nbsp;<a href="#validation.sent">Sending a Validation Request</a></h4><div id="rfc.section.4.3.1.p.1"><p>When generating a conditional request for validation, a cache starts with either a request it is attempting to satisfy, or — if it is initiating the request independently — it synthesises a request using a stored response by copying the method, target URI, and request header fields identified by the Vary header field (<a href="#caching.negotiated.responses" title="Calculating Cache Keys with Vary">Section&nbsp;4.1</a>).</p></div><div id="rfc.section.4.3.1.p.2"><p>It then updates that request with one or more precondition header fields. These contain validator metadata sourced from stored response(s) that have the same cache key.</p></div><div id="rfc.section.4.3.1.p.3"><p>The precondition header fields are then compared by recipients to determine whether any stored response is equivalent to a current representation of the resource.</p></div><div id="rfc.section.4.3.1.p.4"><p>One such validator is the timestamp given in a Last-Modified header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), which can be used in an If-Modified-Since header field for response validation, or in an If-Unmodified-Since or If-Range header field for representation selection (i.e., the client is referring specifically to a previously obtained representation with that timestamp).</p></div><div id="rfc.section.4.3.1.p.5"><p>Another validator is the entity-tag given in an ETag field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>). One or more entity-tags, indicating one or more stored responses, can be used in an If-None-Match header field for response validation, or in an If-Match or If-Range header field for representation selection (i.e., the client is referring specifically to one or more previously obtained representations with the listed entity-tags).</p></div></section><section id="validation.received"><h4 id="rfc.section.4.3.2"><a href="#rfc.section.4.3.2">4.3.2.</a>&nbsp;<a href="#validation.received">Handling a Received Validation Request</a></h4><div id="rfc.section.4.3.2.p.1"><p>Each client in the request chain may have its own cache, so it is common for a cache at an intermediary to receive conditional requests from other (outbound) caches. Likewise, some user agents make use of conditional requests to limit data transfers to recently modified representations or to complete the transfer of a partially retrieved representation.</p></div><div id="rfc.section.4.3.2.p.2"><p>If a cache receives a request that can be satisfied by reusing one of its stored 200 (OK) or 206 (Partial Content) responses, the cache SHOULD evaluate any applicable conditional header field preconditions received in that request with respect to the corresponding validators contained within the selected response. A cache MUST NOT evaluate conditional header fields that only apply to an origin server, occur in a request with semantics that cannot be satisfied with a cached response, or occur in a request with a target resource for which it has no stored responses; such preconditions are likely intended for some other (inbound) server.</p></div><div id="rfc.section.4.3.2.p.3"><p>The proper evaluation of conditional requests by a cache depends on the received precondition header fields and their precedence. In summary, the If-Match and If-Unmodified-Since conditional header fields are not applicable to a cache, and If-None-Match takes precedence over If-Modified-Since. See Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a> for a complete specification of precondition precedence.</p></div><div id="rfc.section.4.3.2.p.4"><p>A request containing an If-None-Match header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) indicates that the client wants to validate one or more of its own stored responses in comparison to whichever stored response is selected by the cache.</p></div><div id="rfc.section.4.3.2.p.5"><p>When a cache decides to revalidate its own stored responses for a request that contains an If-None-Match list of entity-tags, the cache MAY combine the received list with a list of entity-tags from its own stored set of responses (fresh or stale) and send the union of the two lists as a replacement If-None-Match header field value in the forwarded request. If a stored response contains only partial content, the cache MUST NOT include its entity-tag in the union unless the request is for a range that would be fully satisfied by that partial stored response. If the response to the forwarded request is 304 (Not Modified) and has an ETag field value with an entity-tag that is not in the client's list, the cache MUST generate a 200 (OK) response for the client by reusing its corresponding stored response, as updated by the 304 response metadata (<a href="#freshening.responses" title="Freshening Stored Responses upon Validation">Section&nbsp;4.3.4</a>).</p></div><div id="rfc.section.4.3.2.p.6"><p>If an If-None-Match header field is not present, a request containing an If-Modified-Since header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) indicates that the client wants to validate one or more of its own stored responses by modification date.</p></div><div id="rfc.section.4.3.2.p.7"><p>If a request contains an If-Modified-Since header field and the Last-Modified header field is not present in a selected stored response, a cache SHOULD use the stored response's Date field value (or, if no Date field is present, the time that the stored response was received) to evaluate the conditional.</p></div><div id="rfc.section.4.3.2.p.8"><p>A cache that implements partial responses to range requests, as defined in Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>, also needs to evaluate a received If-Range header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) regarding its selected stored response.</p></div></section><section id="validation.response"><h4 id="rfc.section.4.3.3"><a href="#rfc.section.4.3.3">4.3.3.</a>&nbsp;<a href="#validation.response">Handling a Validation Response</a></h4><div id="rfc.section.4.3.3.p.1" class="avoidbreakafter"><p>Cache handling of a response to a conditional request depends upon its status code:</p></div><div id="rfc.section.4.3.3.p.2"><ul><li>A 304 (Not Modified) response status code indicates that the stored response can be updated and reused; see <a href="#freshening.responses" title="Freshening Stored Responses upon Validation">Section&nbsp;4.3.4</a>.</li><li>A full response (i.e., one containing content) indicates that none of the stored responses nominated in the conditional request is suitable. Instead, the cache MUST use the full response to satisfy the request. The cache MAY store such a full response, subject to its constraints (see <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>).</li><li>However, if a cache receives a 5xx (Server Error) response while attempting to validate a response, it can either forward this response to the requesting client, or act as if the server failed to respond. In the latter case, the cache can send a previously stored response, subject to its constraints on doing so (see <a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>), or retry the validation request.</li></ul></div></section><section id="freshening.responses"><h4 id="rfc.section.4.3.4"><a href="#rfc.section.4.3.4">4.3.4.</a>&nbsp;<a href="#freshening.responses">Freshening Stored Responses upon Validation</a></h4><div id="rfc.section.4.3.4.p.1"><p>When a cache receives a 304 (Not Modified) response and already has one or more stored 200 (OK) responses for the applicable cache key, the cache needs to identify which (if any) are to be updated by the new information provided, and then do so.</p></div><div id="rfc.iref.s.3"></div><div id="rfc.section.4.3.4.p.2" class="avoidbreakafter"><p>The stored response(s) to update are identified by using the first match (if any) of:</p></div><div id="rfc.section.4.3.4.p.3"><ul><li>If the new response contains one or more strong validators (see Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), then each of those strong validators identify the selected representation for update. All the stored responses with one of those same strong validators are identified for update. If none of the stored responses contain at least one of the same strong validators, then the cache MUST NOT use the new response to update any stored responses.</li><li>If the new response contains no strong validators but does contain one or more weak validators, and those validators correspond to one of the cache's stored responses, then the most recent of those matching stored responses is identified for update.</li><li>If the new response does not include any form of validator (such as where a client generates an If-Modified-Since request from a source other than the Last-Modified response header field), and there is only one stored response, and that stored response also lacks a validator, then that stored response is identified for update.</li></ul></div><div id="rfc.section.4.3.4.p.4"><p>For each stored response identified, the cache MUST update its header fields with the header fields provided in the 304 (Not Modified) response, as per <a href="#update" title="Updating Stored Header Fields">Section&nbsp;3.2</a>.</p></div></section><section id="head.effects"><h4 id="rfc.section.4.3.5"><a href="#rfc.section.4.3.5">4.3.5.</a>&nbsp;<a href="#head.effects">Freshening Responses with HEAD</a></h4><div id="rfc.section.4.3.5.p.1"><p>A response to the HEAD method is identical to what an equivalent request made with a GET would have been, without sending the content. This property of HEAD responses can be used to invalidate or update a cached GET response if the more efficient conditional GET request mechanism is not available (due to no validators being present in the stored response) or if transmission of the content is not desired even if it has changed.</p></div><div id="rfc.section.4.3.5.p.2"><p>When a cache makes an inbound HEAD request for a target URI and receives a 200 (OK) response, the cache SHOULD update or invalidate each of its stored GET responses that could have been selected for that request (see <a href="#caching.negotiated.responses" title="Calculating Cache Keys with Vary">Section&nbsp;4.1</a>).</p></div><div id="rfc.section.4.3.5.p.3"><p>For each of the stored responses that could have been selected, if the stored response and HEAD response have matching values for any received validator fields (ETag and Last-Modified) and, if the HEAD response has a Content-Length header field, the value of Content-Length matches that of the stored response, the cache SHOULD update the stored response as described below; otherwise, the cache SHOULD consider the stored response to be stale.</p></div><div id="rfc.section.4.3.5.p.4"><p>If a cache updates a stored response with the metadata provided in a HEAD response, the cache MUST use the header fields provided in the HEAD response to update the stored response (see <a href="#update" title="Updating Stored Header Fields">Section&nbsp;3.2</a>).</p></div></section></section><section id="invalidation"><h3 id="rfc.section.4.4"><a href="#rfc.section.4.4">4.4.</a>&nbsp;<a href="#invalidation">Invalidating Stored Responses</a></h3><div id="rfc.section.4.4.p.1"><p>Because unsafe request methods (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) such as PUT, POST or DELETE have the potential for changing state on the origin server, intervening caches are required to invalidate stored responses to keep their contents up to date.</p></div><div id="rfc.section.4.4.p.2"><p>A cache MUST invalidate the target URI (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) when a non-error status code is received in response to an unsafe request method (including methods whose safety is unknown).</p></div><div id="rfc.section.4.4.p.3"><p>A cache MAY invalidate other URIs when a non-error status code is received in response to an unsafe request method (including methods whose safety is unknown). In particular, the URI(s) in the Location and Content-Location response header fields (if present) are candidates for invalidation; other URIs might be discovered through mechanisms not specified in this document. However, a cache MUST NOT trigger an invalidation under these conditions if the origin (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>) of the URI to be invalidated differs from that of the target URI (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>). This helps prevent denial-of-service attacks.</p></div><div id="rfc.section.4.4.p.4"><p>Invalidate means that the cache will either remove all stored responses whose target URI matches the given URI, or will mark them as "invalid" and in need of a mandatory validation before they can be sent in response to a subsequent request.</p></div><div id="rfc.section.4.4.p.5"><p>A "non-error response" is one with a 2xx (Successful) or 3xx (Redirection) status code.</p></div><div id="rfc.section.4.4.p.6"><p>Note that this does not guarantee that all appropriate responses are invalidated globally; a state-changing request would only invalidate responses in the caches it travels through.</p></div></section></section><section id="header.field.definitions"><h2 id="rfc.section.5"><a href="#rfc.section.5">5.</a>&nbsp;<a href="#header.field.definitions">Field Definitions</a></h2><div id="rfc.section.5.p.1"><p>This section defines the syntax and semantics of HTTP fields related to caching.</p></div><section id="field.age"><h3 id="rfc.section.5.1"><a href="#rfc.section.5.1">5.1.</a>&nbsp;<a href="#field.age">Age</a></h3><div id="rfc.section.5.1.p.1"><p>The "Age" response header field conveys the sender's estimate of the time since the response was generated or successfully validated at the origin server. Age values are calculated as specified in <a href="#age.calculations" title="Calculating Age">Section&nbsp;4.2.3</a>.</p></div><div id="rfc.figure.u.7"><div id="rfc.iref.g.2"></div><pre class="inline">
+  Age = delta-seconds
+</pre></div><div id="rfc.section.5.1.p.2"><p>The Age field value is a non-negative integer, representing time in seconds (see <a href="#delta-seconds" title="Delta Seconds">Section&nbsp;1.3</a>).</p></div><div id="rfc.section.5.1.p.3"><p>Although it is defined as a singleton header field, a cache encountering a message with multiple Age field lines SHOULD use the first field line, discarding subsequent ones.</p></div><div id="rfc.section.5.1.p.4"><p>If the field value (after discarding additional lines, as per above) is invalid (e.g., it contains a list or something other than a non-negative integer), a cache SHOULD consider the response to be stale.</p></div><div id="rfc.section.5.1.p.5"><p>The presence of an Age header field implies that the response was not generated or validated by the origin server for this request. However, lack of an Age header field does not imply the origin was contacted.</p></div></section><section id="field.cache-control"><h3 id="rfc.section.5.2"><a href="#rfc.section.5.2">5.2.</a>&nbsp;<a href="#field.cache-control">Cache-Control</a></h3><div id="rfc.section.5.2.p.1"><p>The "Cache-Control" header field is used to list directives for caches along the request/response chain. Such cache directives are unidirectional in that the presence of a directive in a request does not imply that the same directive is present in the response, or to be repeated in it.</p></div><div id="rfc.section.5.2.p.2"><p>See <a href="#cache.control.extensions" title="Cache Control Extensions">Section&nbsp;5.2.3</a> for information about how Cache-Control directives defined elsewhere are handled.</p></div><div id="rfc.section.5.2.p.3"><p>A proxy, whether or not it implements a cache, MUST pass cache directives through in forwarded messages, regardless of their significance to that application, since the directives might apply to all recipients along the request/response chain. It is not possible to target a directive to a specific cache.</p></div><div id="rfc.section.5.2.p.4"><p>Cache directives are identified by a token, to be compared case-insensitively, and have an optional argument that can use both token and quoted-string syntax. For the directives defined below that define arguments, recipients ought to accept both forms, even if a specific form is required for generation.</p></div><div id="rfc.figure.u.8"><div id="rfc.iref.g.3"></div><div id="rfc.iref.g.4"></div><pre class="inline">
+  Cache-Control   = #cache-directive
+
+  cache-directive = token [ "=" ( token / quoted-string ) ]
+</pre></div><div id="rfc.section.5.2.p.5"><p>For the cache directives defined below, no argument is defined (nor allowed) unless stated otherwise.</p></div><section id="cache-request-directive"><h4 id="rfc.section.5.2.1"><a href="#rfc.section.5.2.1">5.2.1.</a>&nbsp;<a href="#cache-request-directive">Request Cache-Control Directives</a></h4><div id="rfc.section.5.2.1.p.1"><p>This section defines cache request directives. They are advisory; caches MAY implement them, but are not required to.</p></div><section id="cache-request-directive.max-age"><h5 id="rfc.section.5.2.1.1"><a href="#rfc.section.5.2.1.1">5.2.1.1.</a>&nbsp;<a href="#cache-request-directive.max-age">max-age</a></h5><div id="rfc.section.5.2.1.1.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.1.1.p.2"><ul class="empty"><li><a href="#delta-seconds">delta-seconds</a> (see <a href="#delta-seconds" title="Delta Seconds">Section&nbsp;1.3</a>)</li></ul></div><div id="rfc.section.5.2.1.1.p.3"><p>The "max-age" request directive indicates that the client prefers a response whose age is less than or equal to the specified number of seconds. Unless the max-stale request directive is also present, the client does not wish to receive a stale response.</p></div><div id="rfc.section.5.2.1.1.p.4"><p>This directive uses the token form of the argument syntax: e.g., 'max-age=5' not 'max-age="5"'. A sender MUST NOT generate the quoted-string form.</p></div></section><section id="cache-request-directive.max-stale"><h5 id="rfc.section.5.2.1.2"><a href="#rfc.section.5.2.1.2">5.2.1.2.</a>&nbsp;<a href="#cache-request-directive.max-stale">max-stale</a></h5><div id="rfc.section.5.2.1.2.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.1.2.p.2"><ul class="empty"><li><a href="#delta-seconds">delta-seconds</a> (see <a href="#delta-seconds" title="Delta Seconds">Section&nbsp;1.3</a>)</li></ul></div><div id="rfc.section.5.2.1.2.p.3"><p>The "max-stale" request directive indicates that the client will accept a response that has exceeded its freshness lifetime. If a value is present, then the client is willing to accept a response that has exceeded its freshness lifetime by no more than the specified number of seconds. If no value is assigned to max-stale, then the client will accept a stale response of any age.</p></div><div id="rfc.section.5.2.1.2.p.4"><p>This directive uses the token form of the argument syntax: e.g., 'max-stale=10' not 'max-stale="10"'. A sender MUST NOT generate the quoted-string form.</p></div></section><section id="cache-request-directive.min-fresh"><h5 id="rfc.section.5.2.1.3"><a href="#rfc.section.5.2.1.3">5.2.1.3.</a>&nbsp;<a href="#cache-request-directive.min-fresh">min-fresh</a></h5><div id="rfc.section.5.2.1.3.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.1.3.p.2"><ul class="empty"><li><a href="#delta-seconds">delta-seconds</a> (see <a href="#delta-seconds" title="Delta Seconds">Section&nbsp;1.3</a>)</li></ul></div><div id="rfc.section.5.2.1.3.p.3"><p>The "min-fresh" request directive indicates that the client prefers a response whose freshness lifetime is no less than its current age plus the specified time in seconds. That is, the client wants a response that will still be fresh for at least the specified number of seconds.</p></div><div id="rfc.section.5.2.1.3.p.4"><p>This directive uses the token form of the argument syntax: e.g., 'min-fresh=20' not 'min-fresh="20"'. A sender MUST NOT generate the quoted-string form.</p></div></section><section id="cache-request-directive.no-cache"><h5 id="rfc.section.5.2.1.4"><a href="#rfc.section.5.2.1.4">5.2.1.4.</a>&nbsp;<a href="#cache-request-directive.no-cache">no-cache</a></h5><div id="rfc.section.5.2.1.4.p.1"><p>The "no-cache" request directive indicates that the client prefers stored response not be used to satisfy the request without successful validation on the origin server.</p></div></section><section id="cache-request-directive.no-store"><h5 id="rfc.section.5.2.1.5"><a href="#rfc.section.5.2.1.5">5.2.1.5.</a>&nbsp;<a href="#cache-request-directive.no-store">no-store</a></h5><div id="rfc.section.5.2.1.5.p.1"><p>The "no-store" request directive indicates that a cache MUST NOT store any part of either this request or any response to it. This directive applies to both private and shared caches. "MUST NOT store" in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.</p></div><div id="rfc.section.5.2.1.5.p.2"><p>This directive is <em>not</em> a reliable or sufficient mechanism for ensuring privacy. In particular, malicious or compromised caches might not recognize or obey this directive, and communications networks might be vulnerable to eavesdropping.</p></div><div id="rfc.section.5.2.1.5.p.3"><p>Note that if a request containing this directive is satisfied from a cache, the no-store request directive does not apply to the already stored response.</p></div></section><section id="cache-request-directive.no-transform"><h5 id="rfc.section.5.2.1.6"><a href="#rfc.section.5.2.1.6">5.2.1.6.</a>&nbsp;<a href="#cache-request-directive.no-transform">no-transform</a></h5><div id="rfc.section.5.2.1.6.p.1"><p>The "no-transform" request directive indicates that the client is asking for intermediaries to avoid transforming the content, as defined in Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div></section><section id="cache-request-directive.only-if-cached"><h5 id="rfc.section.5.2.1.7"><a href="#rfc.section.5.2.1.7">5.2.1.7.</a>&nbsp;<a href="#cache-request-directive.only-if-cached">only-if-cached</a></h5><div id="rfc.section.5.2.1.7.p.1"><p>The "only-if-cached" request directive indicates that the client only wishes to obtain a stored response. Caches that honor this request directive SHOULD, upon receiving it, either respond using a stored response consistent with the other constraints of the request, or respond with a 504 (Gateway Timeout) status code.</p></div></section></section><section id="cache-response-directive"><h4 id="rfc.section.5.2.2"><a href="#rfc.section.5.2.2">5.2.2.</a>&nbsp;<a href="#cache-response-directive">Response Cache-Control Directives</a></h4><div id="rfc.section.5.2.2.p.1"><p>This section defines cache response directives. A cache MUST obey the Cache-Control directives defined in this section.</p></div><section id="cache-response-directive.max-age"><h5 id="rfc.section.5.2.2.1"><a href="#rfc.section.5.2.2.1">5.2.2.1.</a>&nbsp;<a href="#cache-response-directive.max-age">max-age</a></h5><div id="rfc.section.5.2.2.1.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.2.1.p.2"><ul class="empty"><li><a href="#delta-seconds">delta-seconds</a> (see <a href="#delta-seconds" title="Delta Seconds">Section&nbsp;1.3</a>)</li></ul></div><div id="rfc.section.5.2.2.1.p.3"><p>The "max-age" response directive indicates that the response is to be considered stale after its age is greater than the specified number of seconds.</p></div><div id="rfc.section.5.2.2.1.p.4"><p>This directive uses the token form of the argument syntax: e.g., 'max-age=5' not 'max-age="5"'. A sender MUST NOT generate the quoted-string form.</p></div></section><section id="cache-response-directive.must-revalidate"><h5 id="rfc.section.5.2.2.2"><a href="#rfc.section.5.2.2.2">5.2.2.2.</a>&nbsp;<a href="#cache-response-directive.must-revalidate">must-revalidate</a></h5><div id="rfc.section.5.2.2.2.p.1"><p>The "must-revalidate" response directive indicates that once the response has become stale, a cache MUST NOT reuse that response to satisfy another request until it has been successfully validated by the origin, as defined by <a href="#validation.model" title="Validation">Section&nbsp;4.3</a>.</p></div><div id="rfc.section.5.2.2.2.p.2"><p>The must-revalidate directive is necessary to support reliable operation for certain protocol features. In all circumstances a cache MUST NOT ignore the must-revalidate directive; in particular, if a cache is disconnected, the cache MUST generate an error response rather than reuse the stale response. The generated status code SHOULD be 504 (Gateway Timeout) unless another error status code is more applicable.</p></div><div id="rfc.section.5.2.2.2.p.3"><p>The must-revalidate directive ought to be used by servers if and only if failure to validate a request could cause incorrect operation, such as a silently unexecuted financial transaction.</p></div><div id="rfc.section.5.2.2.2.p.4"><p>The must-revalidate directive also permits a shared cache to reuse a response to a request containing an Authorization header field (Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>), subject to the above requirement on revalidation (<a href="#caching.authenticated.responses" title="Storing Responses to Authenticated Requests">Section&nbsp;3.5</a>).</p></div></section><section id="cache-response-directive.must-understand"><h5 id="rfc.section.5.2.2.3"><a href="#rfc.section.5.2.2.3">5.2.2.3.</a>&nbsp;<a href="#cache-response-directive.must-understand">must-understand</a></h5><div id="rfc.section.5.2.2.3.p.1"><p>The "must-understand" response directive limits caching of the response to a cache that understands and conforms to the requirements for that response's status code.</p></div><div id="rfc.section.5.2.2.3.p.2"><p>Responses containing "must-understand" SHOULD also contain the "no-store" directive; caches that implement "must-understand" SHOULD ignore the "no-store" directive in responses that contain both directives and a status code that the cache understands and conforms to any related caching requirements.</p></div></section><section id="cache-response-directive.no-cache"><h5 id="rfc.section.5.2.2.4"><a href="#rfc.section.5.2.2.4">5.2.2.4.</a>&nbsp;<a href="#cache-response-directive.no-cache">no-cache</a></h5><div id="rfc.section.5.2.2.4.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.2.4.p.2"><ul class="empty"><li>#<a href="#imported.rules">field-name</a></li></ul></div><div id="rfc.section.5.2.2.4.p.3"><p>The "no-cache" response directive, in its unqualified form (without an argument), indicates that the response MUST NOT be used to satisfy any other request without forwarding it for validation and receiving a successful response; see <a href="#validation.model" title="Validation">Section&nbsp;4.3</a>.</p></div><div id="rfc.section.5.2.2.4.p.4"><p>This allows an origin server to prevent a cache from using the response to satisfy a request without contacting it, even by caches that have been configured to send stale responses.</p></div><div id="rfc.section.5.2.2.4.p.5"><p>The qualified form of no-cache response directive, with an argument that lists one or more field names, indicates that a cache MAY use the response to satisfy a subsequent request, subject to any other restrictions on caching, if the listed header fields are excluded from the subsequent response or the subsequent response has been successfully revalidated with the origin server (updating or removing those fields). This allows an origin server to prevent the re-use of certain header fields in a response, while still allowing caching of the rest of the response.</p></div><div id="rfc.section.5.2.2.4.p.6"><p>The field names given are not limited to the set of header fields defined by this specification. Field names are case-insensitive.</p></div><div id="rfc.section.5.2.2.4.p.7"><p>This directive uses the quoted-string form of the argument syntax. A sender SHOULD NOT generate the token form (even if quoting appears not to be needed for single-entry lists).</p></div><div id="rfc.section.5.2.2.4.p.8"><ul class="empty"><li>Note: The qualified form of the directive is often handled by caches as if an unqualified no-cache directive was received; i.e., the special handling for the qualified form is not widely implemented.</li></ul></div></section><section id="cache-response-directive.no-store"><h5 id="rfc.section.5.2.2.5"><a href="#rfc.section.5.2.2.5">5.2.2.5.</a>&nbsp;<a href="#cache-response-directive.no-store">no-store</a></h5><div id="rfc.section.5.2.2.5.p.1"><p>The "no-store" response directive indicates that a cache MUST NOT store any part of either the immediate request or response, and MUST NOT use the response to satisfy any other request.</p></div><div id="rfc.section.5.2.2.5.p.2"><p>This directive applies to both private and shared caches. "MUST NOT store" in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.</p></div><div id="rfc.section.5.2.2.5.p.3"><p>This directive is <em>not</em> a reliable or sufficient mechanism for ensuring privacy. In particular, malicious or compromised caches might not recognize or obey this directive, and communications networks might be vulnerable to eavesdropping.</p></div><div id="rfc.section.5.2.2.5.p.4"><p>Note that the "must-understand" cache directive overrides "no-store" in certain circumstances; see <a href="#cache-response-directive.must-understand" title="must-understand">Section&nbsp;5.2.2.3</a>.</p></div></section><section id="cache-response-directive.no-transform"><h5 id="rfc.section.5.2.2.6"><a href="#rfc.section.5.2.2.6">5.2.2.6.</a>&nbsp;<a href="#cache-response-directive.no-transform">no-transform</a></h5><div id="rfc.section.5.2.2.6.p.1"><p>The "no-transform" response directive indicates that an intermediary (regardless of whether it implements a cache) MUST NOT transform the content, as defined in Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div></section><section id="cache-response-directive.private"><h5 id="rfc.section.5.2.2.7"><a href="#rfc.section.5.2.2.7">5.2.2.7.</a>&nbsp;<a href="#cache-response-directive.private">private</a></h5><div id="rfc.section.5.2.2.7.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.2.7.p.2"><ul class="empty"><li>#<a href="#imported.rules">field-name</a></li></ul></div><div id="rfc.section.5.2.2.7.p.3"><p>The unqualified "private" response directive indicates that a shared cache MUST NOT store the response (i.e., the response is intended for a single user). It also indicates that a private cache MAY store the response, subject the constraints defined in <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>, even if the response would not otherwise be heuristically cacheable by a private cache.</p></div><div id="rfc.section.5.2.2.7.p.4"><p>If a qualified private response directive is present, with an argument that lists one or more field names, then only the listed header fields are limited to a single user: a shared cache MUST NOT store the listed header fields if they are present in the original response, but MAY store the remainder of the response message without those header fields, subject the constraints defined in <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>.</p></div><div id="rfc.section.5.2.2.7.p.5"><p>The field names given are not limited to the set of header fields defined by this specification. Field names are case-insensitive.</p></div><div id="rfc.section.5.2.2.7.p.6"><p>This directive uses the quoted-string form of the argument syntax. A sender SHOULD NOT generate the token form (even if quoting appears not to be needed for single-entry lists).</p></div><div id="rfc.section.5.2.2.7.p.7"><ul class="empty"><li>Note: This usage of the word "private" only controls where the response can be stored; it cannot ensure the privacy of the message content. Also, the qualified form of the directive is often handled by caches as if an unqualified private directive was received; i.e., the special handling for the qualified form is not widely implemented.</li></ul></div></section><section id="cache-response-directive.proxy-revalidate"><h5 id="rfc.section.5.2.2.8"><a href="#rfc.section.5.2.2.8">5.2.2.8.</a>&nbsp;<a href="#cache-response-directive.proxy-revalidate">proxy-revalidate</a></h5><div id="rfc.section.5.2.2.8.p.1"><p>The "proxy-revalidate" response directive indicates that once the response has become stale, a shared cache MUST NOT reuse that response to satisfy another request until it has been successfully validated by the origin, as defined by <a href="#validation.model" title="Validation">Section&nbsp;4.3</a>. This is analogous to must-revalidate (<a href="#cache-response-directive.must-revalidate" title="must-revalidate">Section&nbsp;5.2.2.2</a>), except that proxy-revalidate does not apply to private caches.</p></div><div id="rfc.section.5.2.2.8.p.2"><p>Note that "proxy-revalidate" on its own does not imply that a response is cacheable. For example, it might be combined with the public directive (<a href="#cache-response-directive.public" title="public">Section&nbsp;5.2.2.9</a>), allowing the response to be cached while requiring only a shared cache to revalidate when stale.</p></div></section><section id="cache-response-directive.public"><h5 id="rfc.section.5.2.2.9"><a href="#rfc.section.5.2.2.9">5.2.2.9.</a>&nbsp;<a href="#cache-response-directive.public">public</a></h5><div id="rfc.section.5.2.2.9.p.1"><p>The "public" response directive indicates that a cache MAY store the response even if it would otherwise be prohibited, subject to the constraints defined in <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>. In other words, public explicitly marks the response as cacheable. For example, public permits a shared cache to reuse a response to a request containing an Authorization header field (<a href="#caching.authenticated.responses" title="Storing Responses to Authenticated Requests">Section&nbsp;3.5</a>).</p></div><div id="rfc.section.5.2.2.9.p.2"><p>Note that it is unnecessary to add the public directive to a response that is already cacheable according to <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>.</p></div><div id="rfc.section.5.2.2.9.p.3"><p>If a response with the public directive has no explicit freshness information, it is heuristically cacheable (<a href="#heuristic.freshness" title="Calculating Heuristic Freshness">Section&nbsp;4.2.2</a>).</p></div></section><section id="cache-response-directive.s-maxage"><h5 id="rfc.section.5.2.2.10"><a href="#rfc.section.5.2.2.10">5.2.2.10.</a>&nbsp;<a href="#cache-response-directive.s-maxage">s-maxage</a></h5><div id="rfc.section.5.2.2.10.p.1" class="avoidbreakafter"><p>Argument syntax:</p></div><div id="rfc.section.5.2.2.10.p.2"><ul class="empty"><li><a href="#delta-seconds">delta-seconds</a> (see <a href="#delta-seconds" title="Delta Seconds">Section&nbsp;1.3</a>)</li></ul></div><div id="rfc.section.5.2.2.10.p.3"><p>The "s-maxage" response directive indicates that, for a shared cache, the maximum age specified by this directive overrides the maximum age specified by either the max-age directive or the <a href="#field.expires">Expires</a> header field.</p></div><div id="rfc.section.5.2.2.10.p.4"><p>The s-maxage directive incorporates the proxy-revalidate (<a href="#cache-response-directive.proxy-revalidate" title="proxy-revalidate">Section&nbsp;5.2.2.8</a>) response directive's semantics for a shared cache. A shared cache MUST NOT reuse a stale response with s-maxage to satisfy another request until it has been successfully validated by the origin, as defined by <a href="#validation.model" title="Validation">Section&nbsp;4.3</a>. This directive also permits a shared cache to reuse a response to a request containing an Authorization header field, subject to the above requirements on maximum age and revalidation (<a href="#caching.authenticated.responses" title="Storing Responses to Authenticated Requests">Section&nbsp;3.5</a>).</p></div><div id="rfc.section.5.2.2.10.p.5"><p>This directive uses the token form of the argument syntax: e.g., 's-maxage=10' not 's-maxage="10"'. A sender MUST NOT generate the quoted-string form.</p></div></section></section><section id="cache.control.extensions"><h4 id="rfc.section.5.2.3"><a href="#rfc.section.5.2.3">5.2.3.</a>&nbsp;<a href="#cache.control.extensions">Cache Control Extensions</a></h4><div id="rfc.section.5.2.3.p.1"><p>The Cache-Control header field can be extended through the use of one or more cache-extension tokens, each with an optional value. A cache MUST ignore unrecognized cache directives.</p></div><div id="rfc.section.5.2.3.p.2"><p>Informational extensions (those that do not require a change in cache behavior) can be added without changing the semantics of other directives.</p></div><div id="rfc.section.5.2.3.p.3"><p>Behavioral extensions are designed to work by acting as modifiers to the existing base of cache directives. Both the new directive and the old directive are supplied, such that applications that do not understand the new directive will default to the behavior specified by the old directive, and those that understand the new directive will recognize it as modifying the requirements associated with the old directive. In this way, extensions to the existing cache-control directives can be made without breaking deployed caches.</p></div><div id="rfc.section.5.2.3.p.4"><p>For example, consider a hypothetical new response directive called "community" that acts as a modifier to the private directive: in addition to private caches, any cache that is shared only by members of the named community is allowed to cache the response. An origin server wishing to allow the UCI community to use an otherwise private response in their shared cache(s) could do so by including</p></div><div id="rfc.figure.u.9"><pre class="text">
+  Cache-Control: private, community="UCI"
+</pre></div><div id="rfc.section.5.2.3.p.5"><p>A cache that recognizes such a community cache-extension could broaden its behavior in accordance with that extension. A cache that does not recognize the community cache-extension would ignore it and adhere to the private directive.</p></div><div id="rfc.section.5.2.3.p.6" class="avoidbreakafter"><p>New extension directives ought to consider defining:</p></div><div id="rfc.section.5.2.3.p.7"><ul><li>What it means for a directive to be specified multiple times,</li><li>When the directive does not take an argument, what it means when an argument is present,</li><li>When the directive requires an argument, what it means when it is missing,</li><li>Whether the directive is specific to requests, responses, or able to be used in either.</li></ul></div></section><section id="cache.directive.registry"><h4 id="rfc.section.5.2.4"><a href="#rfc.section.5.2.4">5.2.4.</a>&nbsp;<a href="#cache.directive.registry">Cache Directive Registry</a></h4><div id="rfc.section.5.2.4.p.1"><p>The "Hypertext Transfer Protocol (HTTP) Cache Directive Registry" defines the namespace for the cache directives. It has been created and is now maintained at &lt;<a href="https://www.iana.org/assignments/http-cache-directives">https://www.iana.org/assignments/http-cache-directives</a>&gt;.</p></div><div id="rfc.section.5.2.4.p.2" class="avoidbreakafter"><p>A registration MUST include the following fields:</p></div><div id="rfc.section.5.2.4.p.3"><ul><li>Cache Directive Name</li><li>Pointer to specification text</li></ul></div><div id="rfc.section.5.2.4.p.4"><p>Values to be added to this namespace require IETF Review (see <a href="#RFC8126"><cite title="Guidelines for Writing an IANA Considerations Section in RFCs">[RFC8126]</cite></a>, Section 4.8).</p></div></section></section><section id="field.expires"><h3 id="rfc.section.5.3"><a href="#rfc.section.5.3">5.3.</a>&nbsp;<a href="#field.expires">Expires</a></h3><div id="rfc.section.5.3.p.1"><p>The "Expires" response header field gives the date/time after which the response is considered stale. See <a href="#expiration.model" title="Freshness">Section&nbsp;4.2</a> for further discussion of the freshness model.</p></div><div id="rfc.section.5.3.p.2"><p>The presence of an Expires header field does not imply that the original resource will change or cease to exist at, before, or after that time.</p></div><div id="rfc.section.5.3.p.3"><p>The Expires field value is an HTTP-date timestamp, as defined in Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div><div id="rfc.figure.u.10"><div id="rfc.iref.g.5"></div><pre class="inline">
+  Expires = HTTP-date
+</pre></div><div id="rfc.section.5.3.p.4"><p>For example</p></div><div id="rfc.figure.u.11"><pre class="text">
+  Expires: Thu, 01 Dec 1994 16:00:00 GMT
+</pre></div><div id="rfc.section.5.3.p.5"><p>A cache recipient MUST interpret invalid date formats, especially the value "0", as representing a time in the past (i.e., "already expired").</p></div><div id="rfc.section.5.3.p.6"><p>If a response includes a <a href="#field.cache-control">Cache-Control</a> header field with the max-age directive (<a href="#cache-response-directive.max-age" title="max-age">Section&nbsp;5.2.2.1</a>), a recipient MUST ignore the Expires header field. Likewise, if a response includes the s-maxage directive (<a href="#cache-response-directive.s-maxage" title="s-maxage">Section&nbsp;5.2.2.10</a>), a shared cache recipient MUST ignore the Expires header field. In both these cases, the value in Expires is only intended for recipients that have not yet implemented the Cache-Control header field.</p></div><div id="rfc.section.5.3.p.7"><p>An origin server without a clock MUST NOT generate an Expires header field unless its value represents a fixed time in the past (always expired) or its value has been associated with the resource by a system or user with a reliable clock.</p></div><div id="rfc.section.5.3.p.8"><p>Historically, HTTP required the Expires field value to be no more than a year in the future. While longer freshness lifetimes are no longer prohibited, extremely large values have been demonstrated to cause problems (e.g., clock overflows due to use of 32-bit integers for time values), and many caches will evict a response far sooner than that.</p></div></section><section id="field.pragma"><h3 id="rfc.section.5.4"><a href="#rfc.section.5.4">5.4.</a>&nbsp;<a href="#field.pragma">Pragma</a></h3><div id="rfc.section.5.4.p.1"><p>The "Pragma" request header field was defined for HTTP/1.0 caches, so that clients could specify a "no-cache" request (as <a href="#field.cache-control">Cache-Control</a> was not defined until HTTP/1.1).</p></div><div id="rfc.section.5.4.p.2"><p>However, support for Cache-Control is now widespread. As a result, this specification deprecates Pragma.</p></div><div id="rfc.section.5.4.p.3"><ul class="empty"><li>Note: Because the meaning of "Pragma: no-cache" in responses was never specified, it does not provide a reliable replacement for "Cache-Control: no-cache" in them.</li></ul></div></section><section id="field.warning"><h3 id="rfc.section.5.5"><a href="#rfc.section.5.5">5.5.</a>&nbsp;<a href="#field.warning">Warning</a></h3><div id="rfc.section.5.5.p.1"><p>The "Warning" header field was used to carry additional information about the status or transformation of a message that might not be reflected in the status code. This specification obsoletes it, as it is not widely generated or surfaced to users. The information it carried can be gleaned from examining other header fields, such as <a href="#field.age">Age</a>.</p></div></section></section><section id="history.lists"><h2 id="rfc.section.6"><a href="#rfc.section.6">6.</a>&nbsp;<a href="#history.lists">Relationship to Applications and Other Caches</a></h2><div id="rfc.section.6.p.1"><p>Applications using HTTP often specify additional forms of caching. For example, Web browsers often have history mechanisms such as "Back" buttons that can be used to redisplay a representation retrieved earlier in a session.</p></div><div id="rfc.section.6.p.2"><p>Likewise, some Web browsers implement caching of images and other assets within a page view; they may or may not honor HTTP caching semantics.</p></div><div id="rfc.section.6.p.3"><p>The requirements in this specification do not necessarily apply to how applications use data after it is retrieved from a HTTP cache. For example, a history mechanism can display a previous representation even if it has expired, and an application can use cached data in other ways beyond its freshness lifetime.</p></div><div id="rfc.section.6.p.4"><p>This specification does not prohibit the application from taking HTTP caching into account; for example, a history mechanism might tell the user that a view is stale, or it might honor cache directives (e.g., Cache-Control: no-store).</p></div><div id="rfc.section.6.p.5"><p>However, when an application caches data and does not make this apparent to or easily controllable by the user, it is strongly encouraged to define its operation with respect to HTTP cache directives, so as not to surprise authors who expect caching semantics to be honoured. For example, while it might be reasonable to define an application cache "above" HTTP that allows a response containing Cache-Control: no-store to be reused for requests that are directly related to the request that fetched it (such as those created during the same page load), it would likely be surprising and confusing to users and authors if it were allowed to be reused for requests unrelated in any way to the one from which it was obtained.</p></div></section><section id="security.considerations"><h2 id="rfc.section.7"><a href="#rfc.section.7">7.</a>&nbsp;<a href="#security.considerations">Security Considerations</a></h2><div id="rfc.section.7.p.1"><p>This section is meant to inform developers, information providers, and users of known security concerns specific to HTTP caching. More general security considerations are addressed in "HTTP/1.1" (Section 17 of <a href="#Messaging"><cite title="HTTP Semantics">[Messaging]</cite></a>) and "HTTP Semantics" (Section 11 of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>).</p></div><div id="rfc.section.7.p.2"><p>Caches expose additional potential vulnerabilities, since the contents of the cache represent an attractive target for malicious exploitation. Because cache contents persist after an HTTP request is complete, an attack on the cache can reveal information long after a user believes that the information has been removed from the network. Therefore, cache contents need to be protected as sensitive information.</p></div><section id="cache.poisoning"><h3 id="rfc.section.7.1"><a href="#rfc.section.7.1">7.1.</a>&nbsp;<a href="#cache.poisoning">Cache Poisoning</a></h3><div id="rfc.section.7.1.p.1"><p>Various attacks might be amplified by being stored in a cache. Such "cache poisoning" attacks happen when an attacker uses implementation flaws, elevated privileges, or other techniques to insert a response into a cache. This is especially effective when shared caches are used to distribute malicious content to many clients.</p></div><div id="rfc.section.7.1.p.2"><p>One common attack vector for cache poisoning is to exploit differences in message parsing on proxies and in user agents; see Appendix of <a href="#Messaging"><cite title="HTTP Semantics">[Messaging]</cite></a> for the relevant requirements regarding HTTP/1.1.</p></div></section><section id="security.timing"><h3 id="rfc.section.7.2"><a href="#rfc.section.7.2">7.2.</a>&nbsp;<a href="#security.timing">Timing Attacks</a></h3><div id="rfc.section.7.2.p.1"><p>Because one of the primary uses of a cache is to optimise performance, its use can "leak" information about what resources have been previously requested.</p></div><div id="rfc.section.7.2.p.2"><p>For example, if a user visits a site and their browser caches some of its responses, and then navigates to a second site, that site can attempt to load responses it knows exists on the first site. If they load quickly, it can be assumed that the user has visited that site, or even a specific page on it.</p></div><div id="rfc.section.7.2.p.3"><p>Such "timing attacks" can be mitigated by adding more information to the cache key, such as the identity of the referring site (to prevent the attack described above). This is sometimes called "double keying."</p></div></section><section id="caching.of.sensitive.information"><h3 id="rfc.section.7.3"><a href="#rfc.section.7.3">7.3.</a>&nbsp;<a href="#caching.of.sensitive.information">Caching of Sensitive Information</a></h3><div id="rfc.section.7.3.p.1"><p>Implementation and deployment flaws (as well as misunderstanding of cache operation) might lead to caching of sensitive information (e.g., authentication credentials) that is thought to be private, exposing it to unauthorized parties.</p></div><div id="rfc.section.7.3.p.2"><p>Note that the Set-Cookie response header field <a href="#RFC6265"><cite title="HTTP State Management Mechanism">[RFC6265]</cite></a> does not inhibit caching; a cacheable response with a Set-Cookie header field can be (and often is) used to satisfy subsequent requests to caches. Servers who wish to control caching of these responses are encouraged to emit appropriate Cache-Control response header fields.</p></div></section></section><section id="iana.considerations"><h2 id="rfc.section.8"><a href="#rfc.section.8">8.</a>&nbsp;<a href="#iana.considerations">IANA Considerations</a></h2><div id="rfc.section.8.p.1"><p>The change controller for the following registrations is: "IETF (iesg@ietf.org) - Internet Engineering Task Force".</p></div><section id="field.name.registration"><h3 id="rfc.section.8.1"><a href="#rfc.section.8.1">8.1.</a>&nbsp;<a href="#field.name.registration">Field Name Registration</a></h3><div id="rfc.section.8.1.p.1"><p>First, introduce the new "Hypertext Transfer Protocol (HTTP) Field Name Registry" at &lt;<a href="https://www.iana.org/assignments/http-fields">https://www.iana.org/assignments/http-fields</a>&gt; as described in Section 12.1 of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div><div id="rfc.section.8.1.p.2" class="avoidbreakafter"><p>Then, please update the registry with the field names listed in the table below:</p></div><div id="rfc.table.1" class="table table-condensed table-striped"><div id="iana.header.registration.table"></div><table class="table table-condensed table-striped full tleft"><caption>Table 1</caption><thead><tr><th class="left">Field Name</th><th class="left">Status</th><th class="left">Ref.</th><th class="left">Comments</th></tr></thead><tbody><tr><td class="text-left">Age</td><td class="text-left">standard</td><td class="text-left"><a href="#field.age" title="Age">5.1</a> </td><td class="text-left"></td></tr><tr><td class="text-left">Cache-Control</td><td class="text-left">standard</td><td class="text-left"><a href="#field.cache-control" title="Cache-Control">5.2</a> </td><td class="text-left"></td></tr><tr><td class="text-left">Expires</td><td class="text-left">standard</td><td class="text-left"><a href="#field.expires" title="Expires">5.3</a> </td><td class="text-left"></td></tr><tr><td class="text-left">Pragma</td><td class="text-left">standard</td><td class="text-left"><a href="#field.pragma" title="Pragma">5.4</a> </td><td class="text-left"></td></tr><tr><td class="text-left">Warning</td><td class="text-left">obsoleted</td><td class="text-left"><a href="#field.warning" title="Warning">5.5</a> </td><td class="text-left"></td></tr></tbody></table></div></section><section id="cache.directive.registration"><h3 id="rfc.section.8.2"><a href="#rfc.section.8.2">8.2.</a>&nbsp;<a href="#cache.directive.registration">Cache Directive Registration</a></h3><div id="rfc.section.8.2.p.1"><p>Please update the "Hypertext Transfer Protocol (HTTP) Cache Directive Registry" at &lt;<a href="https://www.iana.org/assignments/http-cache-directives">https://www.iana.org/assignments/http-cache-directives</a>&gt; with the registration procedure of <a href="#cache.directive.registry" title="Cache Directive Registry">Section&nbsp;5.2.4</a> and the cache directive names summarized in the table below.</p></div><div id="rfc.table.2" class="table table-condensed table-striped"><div id="iana.cache.directive.registration.table"></div><table class="table table-condensed table-striped full tleft"><caption>Table 2</caption><thead><tr><th class="left">Cache Directive</th><th class="left">Reference</th></tr></thead><tbody><tr><td class="text-left">max-age</td><td class="text-left"><a href="#cache-request-directive.max-age" title="max-age">Section&nbsp;5.2.1.1</a>, <a href="#cache-response-directive.max-age" title="max-age">Section&nbsp;5.2.2.1</a> </td></tr><tr><td class="text-left">max-stale</td><td class="text-left"><a href="#cache-request-directive.max-stale" title="max-stale">Section&nbsp;5.2.1.2</a> </td></tr><tr><td class="text-left">min-fresh</td><td class="text-left"><a href="#cache-request-directive.min-fresh" title="min-fresh">Section&nbsp;5.2.1.3</a> </td></tr><tr><td class="text-left">must-revalidate</td><td class="text-left"><a href="#cache-response-directive.must-revalidate" title="must-revalidate">Section&nbsp;5.2.2.2</a> </td></tr><tr><td class="text-left">must-understand</td><td class="text-left"><a href="#cache-response-directive.must-understand" title="must-understand">Section&nbsp;5.2.2.3</a> </td></tr><tr><td class="text-left">no-cache</td><td class="text-left"><a href="#cache-request-directive.no-cache" title="no-cache">Section&nbsp;5.2.1.4</a>, <a href="#cache-response-directive.no-cache" title="no-cache">Section&nbsp;5.2.2.4</a> </td></tr><tr><td class="text-left">no-store</td><td class="text-left"><a href="#cache-request-directive.no-store" title="no-store">Section&nbsp;5.2.1.5</a>, <a href="#cache-response-directive.no-store" title="no-store">Section&nbsp;5.2.2.5</a> </td></tr><tr><td class="text-left">no-transform</td><td class="text-left"><a href="#cache-request-directive.no-transform" title="no-transform">Section&nbsp;5.2.1.6</a>, <a href="#cache-response-directive.no-transform" title="no-transform">Section&nbsp;5.2.2.6</a> </td></tr><tr><td class="text-left">only-if-cached</td><td class="text-left"><a href="#cache-request-directive.only-if-cached" title="only-if-cached">Section&nbsp;5.2.1.7</a> </td></tr><tr><td class="text-left">private</td><td class="text-left"><a href="#cache-response-directive.private" title="private">Section&nbsp;5.2.2.7</a> </td></tr><tr><td class="text-left">proxy-revalidate</td><td class="text-left"><a href="#cache-response-directive.proxy-revalidate" title="proxy-revalidate">Section&nbsp;5.2.2.8</a> </td></tr><tr><td class="text-left">public</td><td class="text-left"><a href="#cache-response-directive.public" title="public">Section&nbsp;5.2.2.9</a> </td></tr><tr><td class="text-left">s-maxage</td><td class="text-left"><a href="#cache-response-directive.s-maxage" title="s-maxage">Section&nbsp;5.2.2.10</a> </td></tr></tbody></table></div></section><section id="warn.code.registration"><h3 id="rfc.section.8.3"><a href="#rfc.section.8.3">8.3.</a>&nbsp;<a href="#warn.code.registration">Warn Code Registry</a></h3><div id="rfc.section.8.3.p.1"><p>Please add a note to the "Hypertext Transfer Protocol (HTTP) Warn Codes" registry at &lt;<a href="https://www.iana.org/assignments/http-warn-codes">https://www.iana.org/assignments/http-warn-codes</a>&gt; to the effect that Warning is obsoleted.</p></div></section></section><section id="rfc.references"><h2 id="rfc.section.9"><a href="#rfc.section.9">9.</a> References</h2><section id="rfc.references.1"><h3 id="rfc.section.9.1"><a href="#rfc.section.9.1">9.1.</a> Normative References</h3><dl class="dl-horizontal"><dt id="Messaging">[Messaging]</dt><dd>Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “<a href="https://tools.ietf.org/html/rfc9110">HTTP Semantics</a>”, RFC&nbsp;9110, <a href="http://dx.doi.org/10.17487/RFC9110">DOI&nbsp;10.17487/RFC9110</a>, June&nbsp;2022, &lt;<a href="https://www.rfc-editor.org/info/rfc9110">https://www.rfc-editor.org/info/rfc9110</a>&gt;.</dd><dt id="RFC2119">[RFC2119]</dt><dd>Bradner, S., “<a href="https://tools.ietf.org/html/rfc2119">Key words for use in RFCs to Indicate Requirement Levels</a>”, BCP&nbsp;14, RFC&nbsp;2119, <a href="http://dx.doi.org/10.17487/RFC2119">DOI&nbsp;10.17487/RFC2119</a>, March&nbsp;1997, &lt;<a href="https://www.rfc-editor.org/info/rfc2119">https://www.rfc-editor.org/info/rfc2119</a>&gt;.</dd><dt id="RFC5234">[RFC5234]</dt><dd>Crocker, D., Ed. and P. Overell, “<a href="https://tools.ietf.org/html/rfc5234">Augmented BNF for Syntax Specifications: ABNF</a>”, STD&nbsp;68, RFC&nbsp;5234, <a href="http://dx.doi.org/10.17487/RFC5234">DOI&nbsp;10.17487/RFC5234</a>, January&nbsp;2008, &lt;<a href="https://www.rfc-editor.org/info/rfc5234">https://www.rfc-editor.org/info/rfc5234</a>&gt;.</dd><dt id="RFC7405">[RFC7405]</dt><dd>Kyzivat, P., “<a href="https://tools.ietf.org/html/rfc7405">Case-Sensitive String Support in ABNF</a>”, RFC&nbsp;7405, <a href="http://dx.doi.org/10.17487/RFC7405">DOI&nbsp;10.17487/RFC7405</a>, December&nbsp;2014, &lt;<a href="https://www.rfc-editor.org/info/rfc7405">https://www.rfc-editor.org/info/rfc7405</a>&gt;.</dd><dt id="RFC8174">[RFC8174]</dt><dd>Leiba, B., “<a href="https://tools.ietf.org/html/rfc8174">Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</a>”, BCP&nbsp;14, RFC&nbsp;8174, <a href="http://dx.doi.org/10.17487/RFC8174">DOI&nbsp;10.17487/RFC8174</a>, May&nbsp;2017, &lt;<a href="https://www.rfc-editor.org/info/rfc8174">https://www.rfc-editor.org/info/rfc8174</a>&gt;.</dd><dt id="Semantics">[Semantics]</dt><dd>Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “<a href="https://tools.ietf.org/html/rfc9112">HTTP/1.1</a>”, RFC&nbsp;9112, <a href="http://dx.doi.org/10.17487/RFC9112">DOI&nbsp;10.17487/RFC9112</a>, June&nbsp;2022, &lt;<a href="https://www.rfc-editor.org/info/rfc9112">https://www.rfc-editor.org/info/rfc9112</a>&gt;.</dd></dl></section><section id="rfc.references.2"><h3 id="rfc.section.9.2"><a href="#rfc.section.9.2">9.2.</a> Informative References</h3><dl class="dl-horizontal"><dt id="RFC2616">[RFC2616]</dt><dd>Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, “<a href="https://tools.ietf.org/html/rfc2616">Hypertext Transfer Protocol -- HTTP/1.1</a>”, RFC&nbsp;2616, <a href="http://dx.doi.org/10.17487/RFC2616">DOI&nbsp;10.17487/RFC2616</a>, June&nbsp;1999, &lt;<a href="https://www.rfc-editor.org/info/rfc2616">https://www.rfc-editor.org/info/rfc2616</a>&gt;.</dd><dt id="RFC5861">[RFC5861]</dt><dd>Nottingham, M., “<a href="https://tools.ietf.org/html/rfc5861">HTTP Cache-Control Extensions for Stale Content</a>”, RFC&nbsp;5861, <a href="http://dx.doi.org/10.17487/RFC5861">DOI&nbsp;10.17487/RFC5861</a>, April&nbsp;2010, &lt;<a href="https://www.rfc-editor.org/info/rfc5861">https://www.rfc-editor.org/info/rfc5861</a>&gt;.</dd><dt id="RFC5905">[RFC5905]</dt><dd>Mills, D., Martin, J., Ed., Burbank, J., and W. Kasch, “<a href="https://tools.ietf.org/html/rfc5905">Network Time Protocol Version 4: Protocol and Algorithms Specification</a>”, RFC&nbsp;5905, <a href="http://dx.doi.org/10.17487/RFC5905">DOI&nbsp;10.17487/RFC5905</a>, June&nbsp;2010, &lt;<a href="https://www.rfc-editor.org/info/rfc5905">https://www.rfc-editor.org/info/rfc5905</a>&gt;.</dd><dt id="RFC6265">[RFC6265]</dt><dd>Barth, A., “<a href="https://tools.ietf.org/html/rfc6265">HTTP State Management Mechanism</a>”, RFC&nbsp;6265, <a href="http://dx.doi.org/10.17487/RFC6265">DOI&nbsp;10.17487/RFC6265</a>, April&nbsp;2011, &lt;<a href="https://www.rfc-editor.org/info/rfc6265">https://www.rfc-editor.org/info/rfc6265</a>&gt;.</dd><dt id="RFC7234">[RFC7234]</dt><dd>Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “<a href="https://tools.ietf.org/html/rfc7234">Hypertext Transfer Protocol (HTTP): Caching</a>”, RFC&nbsp;7234, <a href="http://dx.doi.org/10.17487/RFC7234">DOI&nbsp;10.17487/RFC7234</a>, June&nbsp;2014, &lt;<a href="https://www.rfc-editor.org/info/rfc7234">https://www.rfc-editor.org/info/rfc7234</a>&gt;.</dd><dt id="RFC8126">[RFC8126]</dt><dd>Cotton, M., Leiba, B., and T. Narten, “<a href="https://tools.ietf.org/html/rfc8126">Guidelines for Writing an IANA Considerations Section in RFCs</a>”, BCP&nbsp;26, RFC&nbsp;8126, <a href="http://dx.doi.org/10.17487/RFC8126">DOI&nbsp;10.17487/RFC8126</a>, June&nbsp;2017, &lt;<a href="https://www.rfc-editor.org/info/rfc8126">https://www.rfc-editor.org/info/rfc8126</a>&gt;.</dd></dl></section></section><section id="collected.abnf"><h2 id="rfc.section.A" class="np"><a href="#rfc.section.A">A.</a>&nbsp;<a href="#collected.abnf">Collected ABNF</a></h2><div id="rfc.section.A.p.1"><p>In the collected ABNF below, list rules are expanded as per Appendix of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div><div id="rfc.figure.u.12"><pre class="inline">
+Age = delta-seconds
+
+Cache-Control = [ cache-directive *( OWS "," OWS cache-directive ) ]
+
+Expires = HTTP-date
+
+HTTP-date = &lt;HTTP-date, see [Semantics], Section 5.6.7&gt;
+
+OWS = &lt;OWS, see [Semantics], Section 5.6.3&gt;
+
+cache-directive = token [ "=" ( token / quoted-string ) ]
+
+delta-seconds = 1*DIGIT
+
+field-name = &lt;field-name, see [Semantics], Section 5.1&gt;
+
+quoted-string = &lt;quoted-string, see [Semantics], Section 5.6.4&gt;
+
+token = &lt;token, see [Semantics], Section 5.6.2&gt;
+</pre></div></section><section id="changes.from.rfc.7234"><h2 id="rfc.section.B"><a href="#rfc.section.B">B.</a>&nbsp;<a href="#changes.from.rfc.7234">Changes from RFC 7234</a></h2><div id="rfc.section.B.p.1"><p>Handling of duplicate and conflicting cache directives has been clarified. (<a href="#calculating.freshness.lifetime" title="Calculating Freshness Lifetime">Section&nbsp;4.2.1</a>)</p></div><div id="rfc.section.B.p.2"><p>Cache invalidation of the URIs in the Location and Content-Location header fields is no longer required, but still allowed. (<a href="#invalidation" title="Invalidating Stored Responses">Section&nbsp;4.4</a>)</p></div><div id="rfc.section.B.p.3"><p>Cache invalidation of the URIs in the Location and Content-Location header fields is disallowed when the origin is different; previously, it was the host. (<a href="#invalidation" title="Invalidating Stored Responses">Section&nbsp;4.4</a>)</p></div><div id="rfc.section.B.p.4"><p>Handling invalid and multiple Age header field values has been clarified. (<a href="#field.age" title="Age">Section&nbsp;5.1</a>)</p></div><div id="rfc.section.B.p.5"><p>Some cache directives defined by this specification now have stronger prohibitions against generating the quoted form of their values, since this has been found to create interoperability problems. Consumers of extension cache directives are no longer required to accept both token and quoted-string forms, but they still need to parse them properly for unknown extensions. (<a href="#field.cache-control" title="Cache-Control">Section&nbsp;5.2</a>)</p></div><div id="rfc.section.B.p.6"><p>The "public" and "private" cache directives were clarified, so that they do not make responses reusable under any condition. (<a href="#cache-response-directive" title="Response Cache-Control Directives">Section&nbsp;5.2.2</a>)</p></div><div id="rfc.section.B.p.7"><p>The "must-understand" cache directive was introduced; caches are no longer required to understand the semantics of new response status codes unless it is present. (<a href="#cache-response-directive.must-understand" title="must-understand">Section&nbsp;5.2.2.3</a>)</p></div><div id="rfc.section.B.p.8"><p>The Warning response header was obsoleted. Much of the information supported by Warning could be gleaned by examining the response, and the remaining warn-codes — although potentially useful — were entirely advisory. In practice, Warning was not added by caches or intermediaries. (<a href="#field.warning" title="Warning">Section&nbsp;5.5</a>)</p></div></section><section id="change.log"><h2 id="rfc.section.C"><a href="#rfc.section.C">C.</a>&nbsp;<a href="#change.log">Change Log</a></h2><div id="rfc.section.C.p.1"><p>This section is to be removed before publishing as an RFC.</p></div><section id="changes.since.publication.as.rfc"><h3 id="rfc.section.C.1"><a href="#rfc.section.C.1">C.1.</a>&nbsp;<a href="#changes.since.publication.as.rfc">Between RFC7234 and draft 00</a></h3><div id="rfc.section.C.1.p.1" class="avoidbreakafter"><p>The changes were purely editorial:</p></div><div id="rfc.section.C.1.p.2"><ul><li>Change boilerplate and abstract to indicate the "draft" status, and update references to ancestor specifications.</li><li>Remove version "1.1" from document title, indicating that this specification applies to all HTTP versions.</li><li>Adjust historical notes.</li><li>Update links to sibling specifications.</li><li>Replace sections listing changes from RFC 2616 by new empty sections referring to RFC 723x.</li><li>Remove acknowledgements specific to RFC 723x.</li><li>Move "Acknowledgements" to the very end and make them unnumbered.</li></ul></div></section><section id="changes.since.00"><h3 id="rfc.section.C.2"><a href="#rfc.section.C.2">C.2.</a>&nbsp;<a href="#changes.since.00">Since draft-ietf-httpbis-cache-00</a></h3><div id="rfc.section.C.2.p.1" class="avoidbreakafter"><p>The changes are purely editorial:</p></div><div id="rfc.section.C.2.p.2"><ul><li>Moved all extensibility tips, registration procedures, and registry tables from the IANA considerations to normative sections, reducing the IANA considerations to just instructions that will be removed prior to publication as an RFC.</li></ul></div></section><section id="changes.since.01"><h3 id="rfc.section.C.3"><a href="#rfc.section.C.3">C.3.</a>&nbsp;<a href="#changes.since.01">Since draft-ietf-httpbis-cache-01</a></h3><div id="rfc.section.C.3.p.1"><ul><li>Cite RFC 8126 instead of RFC 5226 (&lt;<a href="https://github.com/httpwg/http-core/issues/75">https://github.com/httpwg/http-core/issues/75</a>&gt;)</li><li>In <a href="#field.pragma" title="Pragma">Section&nbsp;5.4</a>, misleading statement about the relation between Pragma and Cache-Control (&lt;<a href="https://github.com/httpwg/http-core/issues/92">https://github.com/httpwg/http-core/issues/92</a>&gt;, &lt;<a href="https://www.rfc-editor.org/errata/eid4674">https://www.rfc-editor.org/errata/eid4674</a>&gt;)</li></ul></div></section><section id="changes.since.02"><h3 id="rfc.section.C.4"><a href="#rfc.section.C.4">C.4.</a>&nbsp;<a href="#changes.since.02">Since draft-ietf-httpbis-cache-02</a></h3><div id="rfc.section.C.4.p.1"><ul><li>In <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>, explain that only final responses are cacheable (&lt;<a href="https://github.com/httpwg/http-core/issues/29">https://github.com/httpwg/http-core/issues/29</a>&gt;)</li><li>In <a href="#cache-response-directive" title="Response Cache-Control Directives">Section&nbsp;5.2.2</a>, clarify what responses various directives apply to (&lt;<a href="https://github.com/httpwg/http-core/issues/52">https://github.com/httpwg/http-core/issues/52</a>&gt;)</li><li>In <a href="#validation.sent" title="Sending a Validation Request">Section&nbsp;4.3.1</a>, clarify the source of validators in conditional requests (&lt;<a href="https://github.com/httpwg/http-core/issues/110">https://github.com/httpwg/http-core/issues/110</a>&gt;)</li><li>Revise <a href="#history.lists" title="Relationship to Applications and Other Caches">Section&nbsp;6</a> to apply to more than just History Lists (&lt;<a href="https://github.com/httpwg/http-core/issues/126">https://github.com/httpwg/http-core/issues/126</a>&gt;)</li><li>In <a href="#field.warning" title="Warning">Section&nbsp;5.5</a>, deprecated "Warning" header field (&lt;<a href="https://github.com/httpwg/http-core/issues/139">https://github.com/httpwg/http-core/issues/139</a>&gt;)</li><li>In <a href="#caching.authenticated.responses" title="Storing Responses to Authenticated Requests">Section&nbsp;3.5</a>, remove a spurious note (&lt;<a href="https://github.com/httpwg/http-core/issues/141">https://github.com/httpwg/http-core/issues/141</a>&gt;)</li></ul></div></section><section id="changes.since.03"><h3 id="rfc.section.C.5"><a href="#rfc.section.C.5">C.5.</a>&nbsp;<a href="#changes.since.03">Since draft-ietf-httpbis-cache-03</a></h3><div id="rfc.section.C.5.p.1"><ul><li>In <a href="#caching.overview" title="Overview of Cache Operation">Section&nbsp;2</a>, define what a disconnected cache is (&lt;<a href="https://github.com/httpwg/http-core/issues/5">https://github.com/httpwg/http-core/issues/5</a>&gt;)</li><li>In <a href="#constructing.responses.from.caches" title="Constructing Responses from Caches">Section&nbsp;4</a>, clarify language around how to select a response when more than one matches (&lt;<a href="https://github.com/httpwg/http-core/issues/23">https://github.com/httpwg/http-core/issues/23</a>&gt;)</li><li>in <a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>, mention stale-while-revalidate and stale-if-error (&lt;<a href="https://github.com/httpwg/http-core/issues/122">https://github.com/httpwg/http-core/issues/122</a>&gt;)</li><li>Remove requirements around cache request directives (&lt;<a href="https://github.com/httpwg/http-core/issues/129">https://github.com/httpwg/http-core/issues/129</a>&gt;)</li><li>Deprecate Pragma (&lt;<a href="https://github.com/httpwg/http-core/issues/140">https://github.com/httpwg/http-core/issues/140</a>&gt;)</li><li>In <a href="#caching.authenticated.responses" title="Storing Responses to Authenticated Requests">Section&nbsp;3.5</a> and <a href="#cache-response-directive" title="Response Cache-Control Directives">Section&nbsp;5.2.2</a>, note effect of some directives on authenticated requests (&lt;<a href="https://github.com/httpwg/http-core/issues/161">https://github.com/httpwg/http-core/issues/161</a>&gt;)</li></ul></div></section><section id="changes.since.04"><h3 id="rfc.section.C.6"><a href="#rfc.section.C.6">C.6.</a>&nbsp;<a href="#changes.since.04">Since draft-ietf-httpbis-cache-04</a></h3><div id="rfc.section.C.6.p.1"><ul><li>In <a href="#field.cache-control" title="Cache-Control">Section&nbsp;5.2</a>, remove the registrations for stale-if-error and stale-while-revalidate which happened in RFC 7234 (&lt;<a href="https://github.com/httpwg/http-core/issues/207">https://github.com/httpwg/http-core/issues/207</a>&gt;)</li></ul></div></section><section id="changes.since.05"><h3 id="rfc.section.C.7"><a href="#rfc.section.C.7">C.7.</a>&nbsp;<a href="#changes.since.05">Since draft-ietf-httpbis-cache-05</a></h3><div id="rfc.section.C.7.p.1"><ul><li>In <a href="#incomplete.responses" title="Storing Incomplete Responses">Section&nbsp;3.3</a>, clarify how weakly framed content is considered for purposes of completeness (&lt;<a href="https://github.com/httpwg/http-core/issues/25">https://github.com/httpwg/http-core/issues/25</a>&gt;)</li><li>Throughout, describe Vary and cache key operations more clearly (&lt;<a href="https://github.com/httpwg/http-core/issues/28">https://github.com/httpwg/http-core/issues/28</a>&gt;)</li><li>In <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>, remove concept of "cacheable methods" in favor of prose (&lt;<a href="https://github.com/httpwg/http-core/issues/54">https://github.com/httpwg/http-core/issues/54</a>&gt;, &lt;<a href="https://www.rfc-editor.org/errata/eid5300">https://www.rfc-editor.org/errata/eid5300</a>&gt;)</li><li>Refactored <a href="#security.considerations" title="Security Considerations">Section&nbsp;7</a>, and added a section on timing attacks (&lt;<a href="https://github.com/httpwg/http-core/issues/233">https://github.com/httpwg/http-core/issues/233</a>&gt;)</li><li>Changed "cacheable by default" to "heuristically cacheable" throughout (&lt;<a href="https://github.com/httpwg/http-core/issues/242">https://github.com/httpwg/http-core/issues/242</a>&gt;)</li></ul></div></section><section id="changes.since.06"><h3 id="rfc.section.C.8"><a href="#rfc.section.C.8">C.8.</a>&nbsp;<a href="#changes.since.06">Since draft-ietf-httpbis-cache-06</a></h3><div id="rfc.section.C.8.p.1"><ul><li>In <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a> and <a href="#cache-response-directive.must-understand" title="must-understand">Section&nbsp;5.2.2.3</a>, change response cacheability to only require understanding the response status code if the must-understand cache directive is present (&lt;<a href="https://github.com/httpwg/http-core/issues/120">https://github.com/httpwg/http-core/issues/120</a>&gt;)</li><li>Change requirements for handling different forms of cache directives in <a href="#field.cache-control" title="Cache-Control">Section&nbsp;5.2</a> (&lt;<a href="https://github.com/httpwg/http-core/issues/128">https://github.com/httpwg/http-core/issues/128</a>&gt;)</li><li>Fix typo in <a href="#cache-response-directive.s-maxage" title="s-maxage">Section&nbsp;5.2.2.10</a> (&lt;<a href="https://github.com/httpwg/http-core/issues/264">https://github.com/httpwg/http-core/issues/264</a>&gt;)</li><li>In <a href="#cache-response-directive.public" title="public">Section&nbsp;5.2.2.9</a> and <a href="#cache-response-directive.private" title="private">Section&nbsp;5.2.2.7</a>, clarify "private" and "public" so that they do not override all other cache directives (&lt;<a href="https://github.com/httpwg/http-core/issues/268">https://github.com/httpwg/http-core/issues/268</a>&gt;)</li><li>In <a href="#response.cacheability" title="Storing Responses in Caches">Section&nbsp;3</a>, distinguish between private with and without qualifying headers (&lt;<a href="https://github.com/httpwg/http-core/issues/270">https://github.com/httpwg/http-core/issues/270</a>&gt;)</li><li>In <a href="#caching.negotiated.responses" title="Calculating Cache Keys with Vary">Section&nbsp;4.1</a>, clarify that any "*" as a member of Vary will disable caching (&lt;<a href="https://github.com/httpwg/http-core/issues/286">https://github.com/httpwg/http-core/issues/286</a>&gt;)</li><li>In <a href="#requirements.notation" title="Requirements Notation">Section&nbsp;1.1</a>, reference RFC 8174 as well (&lt;<a href="https://github.com/httpwg/http-core/issues/303">https://github.com/httpwg/http-core/issues/303</a>&gt;)</li></ul></div></section><section id="changes.since.07"><h3 id="rfc.section.C.9"><a href="#rfc.section.C.9">C.9.</a>&nbsp;<a href="#changes.since.07">Since draft-ietf-httpbis-cache-07</a></h3><div id="rfc.section.C.9.p.1"><ul><li>Throughout, replace "effective request URI", "request-target" and similar with "target URI" (&lt;<a href="https://github.com/httpwg/http-core/issues/259">https://github.com/httpwg/http-core/issues/259</a>&gt;)</li><li>In <a href="#cache-response-directive.public" title="public">Section&nbsp;5.2.2.9</a> and <a href="#cache-response-directive.private" title="private">Section&nbsp;5.2.2.7</a>, make it clear that these directives do not ignore other requirements for caching (&lt;<a href="https://github.com/httpwg/http-core/issues/320">https://github.com/httpwg/http-core/issues/320</a>&gt;)</li><li>In <a href="#incomplete.responses" title="Storing Incomplete Responses">Section&nbsp;3.3</a>, move definition of "complete" into semantics (&lt;<a href="https://github.com/httpwg/http-core/issues/334">https://github.com/httpwg/http-core/issues/334</a>&gt;)</li></ul></div></section><section id="changes.since.08"><h3 id="rfc.section.C.10"><a href="#rfc.section.C.10">C.10.</a>&nbsp;<a href="#changes.since.08">Since draft-ietf-httpbis-cache-08</a></h3><div id="rfc.section.C.10.p.1"><ul><li><a href="#collected.abnf" title="Collected ABNF">Appendix&nbsp;A</a> now uses the sender variant of the "#" list expansion (&lt;<a href="https://github.com/httpwg/http-core/issues/192">https://github.com/httpwg/http-core/issues/192</a>&gt;)</li></ul></div></section><section id="changes.since.09"><h3 id="rfc.section.C.11"><a href="#rfc.section.C.11">C.11.</a>&nbsp;<a href="#changes.since.09">Since draft-ietf-httpbis-cache-09</a></h3><div id="rfc.section.C.11.p.1"><ul><li>In <a href="#field.age" title="Age">Section&nbsp;5.1</a>, discuss handling of invalid and multiple Age header field values (&lt;<a href="https://github.com/httpwg/http-core/issues/193">https://github.com/httpwg/http-core/issues/193</a>&gt;)</li><li>Switch to xml2rfc v3 mode for draft generation (&lt;<a href="https://github.com/httpwg/http-core/issues/394">https://github.com/httpwg/http-core/issues/394</a>&gt;)</li></ul></div></section><section id="changes.since.10"><h3 id="rfc.section.C.12"><a href="#rfc.section.C.12">C.12.</a>&nbsp;<a href="#changes.since.10">Since draft-ietf-httpbis-cache-10</a></h3><div id="rfc.section.C.12.p.1"><ul><li>In <a href="#field.cache-control" title="Cache-Control">Section&nbsp;5.2</a> (<a href="#field.cache-control">Cache-Control</a>), adjust ABNF to allow empty lists (&lt;<a href="https://github.com/httpwg/http-core/issues/210">https://github.com/httpwg/http-core/issues/210</a>&gt;)</li></ul></div></section><section id="changes.since.11"><h3 id="rfc.section.C.13"><a href="#rfc.section.C.13">C.13.</a>&nbsp;<a href="#changes.since.11">Since draft-ietf-httpbis-cache-11</a></h3><div id="rfc.section.C.13.p.1"><p>None.</p></div></section><section id="changes.since.12"><h3 id="rfc.section.C.14"><a href="#rfc.section.C.14">C.14.</a>&nbsp;<a href="#changes.since.12">Since draft-ietf-httpbis-cache-12</a></h3><div id="rfc.section.C.14.p.1"><ul><li>In <a href="#serving.stale.responses" title="Serving Stale Responses">Section&nbsp;4.2.4</a>, remove 'no-store', as it won't be in cache in the first place (&lt;<a href="https://github.com/httpwg/http-core/issues/447">https://github.com/httpwg/http-core/issues/447</a>&gt;)</li><li>In <a href="#storing.fields" title="Storing Header and Trailer Fields">Section&nbsp;3.1</a>, make it clear that only response headers need be stored (&lt;<a href="https://github.com/httpwg/http-core/issues/457">https://github.com/httpwg/http-core/issues/457</a>&gt;)</li><li>Rewrote "Updating Stored Header Fields" <a href="#update" title="Updating Stored Header Fields">Section&nbsp;3.2</a> (&lt;<a href="https://github.com/httpwg/http-core/issues/458">https://github.com/httpwg/http-core/issues/458</a>&gt;)</li><li>In <a href="#calculating.freshness.lifetime" title="Calculating Freshness Lifetime">Section&nbsp;4.2.1</a> clarify how to handle invalid and conflicting directives (&lt;<a href="https://github.com/httpwg/http-core/issues/460">https://github.com/httpwg/http-core/issues/460</a>&gt;)</li><li>In <a href="#validation.response" title="Handling a Validation Response">Section&nbsp;4.3.3</a>, mention retry of failed validation requests (&lt;<a href="https://github.com/httpwg/http-core/issues/462">https://github.com/httpwg/http-core/issues/462</a>&gt;)</li><li>In <a href="#validation.response" title="Handling a Validation Response">Section&nbsp;4.3.3</a>, clarify requirement on storing a full response to a conditional request (&lt;<a href="https://github.com/httpwg/http-core/issues/463">https://github.com/httpwg/http-core/issues/463</a>&gt;)</li><li>In <a href="#field.age" title="Age">Section&nbsp;5.1</a>, clarify error handling (&lt;<a href="https://github.com/httpwg/http-core/issues/471">https://github.com/httpwg/http-core/issues/471</a>&gt;)</li><li>In <a href="#expiration.model" title="Freshness">Section&nbsp;4.2</a>, remove spurious "UTC" (&lt;<a href="https://github.com/httpwg/http-core/issues/472">https://github.com/httpwg/http-core/issues/472</a>&gt;)</li><li>In <a href="#expiration.model" title="Freshness">Section&nbsp;4.2</a>, correct the date-related rule names to consider case-insensitive (&lt;<a href="https://github.com/httpwg/http-core/issues/473">https://github.com/httpwg/http-core/issues/473</a>&gt;)</li><li>In <a href="#history.lists" title="Relationship to Applications and Other Caches">Section&nbsp;6</a>, strengthen recommendation for application caches to pay attention to cache directives (&lt;<a href="https://github.com/httpwg/http-core/issues/474">https://github.com/httpwg/http-core/issues/474</a>&gt;)</li><li>In <a href="#constructing.responses.from.caches" title="Constructing Responses from Caches">Section&nbsp;4</a>, mention collapsed requests (&lt;<a href="https://github.com/httpwg/http-core/issues/475">https://github.com/httpwg/http-core/issues/475</a>&gt;)</li><li>In <a href="#invalidation" title="Invalidating Stored Responses">Section&nbsp;4.4</a>, relax requirements on Content-Location and Location invalidation (&lt;<a href="https://github.com/httpwg/http-core/issues/478">https://github.com/httpwg/http-core/issues/478</a>&gt;)</li><li>In <a href="#freshening.responses" title="Freshening Stored Responses upon Validation">Section&nbsp;4.3.4</a>, refine the exceptions to update on a 304 (&lt;<a href="https://github.com/httpwg/http-core/issues/488">https://github.com/httpwg/http-core/issues/488</a>&gt;)</li><li>Moved table of Cache-Control directives into <a href="#cache.directive.registration" title="Cache Directive Registration">Section&nbsp;8.2</a> (&lt;<a href="https://github.com/httpwg/http-core/issues/506">https://github.com/httpwg/http-core/issues/506</a>&gt;)</li><li>In <a href="#notation" title="Syntax Notation">Section&nbsp;1.2</a>, remove unused core ABNF rules (&lt;<a href="https://github.com/httpwg/http-core/issues/529">https://github.com/httpwg/http-core/issues/529</a>&gt;)</li><li>Changed to using "payload data" when defining requirements about the data being conveyed within a message, instead of the terms "payload body" or "response body" or "representation body", since they often get confused with the HTTP/1.1 message body (which includes transfer coding) (&lt;<a href="https://github.com/httpwg/http-core/issues/553">https://github.com/httpwg/http-core/issues/553</a>&gt;)</li></ul></div></section><section id="changes.since.13"><h3 id="rfc.section.C.15"><a href="#rfc.section.C.15">C.15.</a>&nbsp;<a href="#changes.since.13">Since draft-ietf-httpbis-cache-13</a></h3><div id="rfc.section.C.15.p.1"><ul><li>In <a href="#cache-response-directive.must-revalidate" title="must-revalidate">Section&nbsp;5.2.2.2</a>, clarify requirements around generating an error response (&lt;<a href="https://github.com/httpwg/http-core/issues/608">https://github.com/httpwg/http-core/issues/608</a>&gt;)</li><li>Changed to using "content" instead of "payload" or "payload data" to avoid confusion with the payload of version-specific messaging frames (&lt;<a href="https://github.com/httpwg/http-core/issues/654">https://github.com/httpwg/http-core/issues/654</a>&gt;)</li><li>In <a href="#freshening.responses" title="Freshening Stored Responses upon Validation">Section&nbsp;4.3.4</a>, clarify how multiple validators are handled (&lt;<a href="https://github.com/httpwg/http-core/issues/659">https://github.com/httpwg/http-core/issues/659</a>&gt;)</li><li>In <a href="#age.calculations" title="Calculating Age">Section&nbsp;4.2.3</a>, <a href="#field.cache-control" title="Cache-Control">Section&nbsp;5.2</a>, and <a href="#cache-response-directive.no-cache" title="no-cache">Section&nbsp;5.2.2.4</a>, remove notes about very old HTTP/1.0 behaviours (&lt;<a href="https://github.com/httpwg/http-core/issues/660">https://github.com/httpwg/http-core/issues/660</a>&gt;)</li><li>In <a href="#cache-response-directive.must-understand" title="must-understand">Section&nbsp;5.2.2.3</a>, modify operation to be more backwards-compatible with existing implementations (&lt;<a href="https://github.com/httpwg/http-core/issues/661">https://github.com/httpwg/http-core/issues/661</a>&gt;)</li><li>In <a href="#cache.poisoning" title="Cache Poisoning">Section&nbsp;7.1</a>, cache poisoning can affect private caches too (&lt;<a href="https://github.com/httpwg/http-core/issues/730">https://github.com/httpwg/http-core/issues/730</a>&gt;)</li></ul></div></section><section id="changes.since.14"><h3 id="rfc.section.C.16"><a href="#rfc.section.C.16">C.16.</a>&nbsp;<a href="#changes.since.14">Since draft-ietf-httpbis-cache-14</a></h3><div id="rfc.section.C.16.p.1"><ul><li>Fix subsection ordering in <a href="#cache-response-directive" title="Response Cache-Control Directives">Section&nbsp;5.2.2</a> (&lt;<a href="https://github.com/httpwg/http-core/issues/674">https://github.com/httpwg/http-core/issues/674</a>&gt;)</li><li>In <a href="#caching.overview" title="Overview of Cache Operation">Section&nbsp;2</a>, define what a cache key is (&lt;<a href="https://github.com/httpwg/http-core/issues/728">https://github.com/httpwg/http-core/issues/728</a>&gt;)</li></ul></div></section></section><section id="acks"><h2 id="rfc.section.unnumbered-1"><a href="#acks">Acknowledgments</a></h2><div id="rfc.section.unnumbered-1.p.1"><p>See Appendix "Acknowledgements" of <a href="#Semantics"><cite title="HTTP/1.1">[Semantics]</cite></a>.</p></div></section><section id="rfc.index"><h2><a href="#rfc.index">Index</a></h2><p class="hidden-print"><a href="#rfc.index.A">A</a> <a href="#rfc.index.C">C</a> <a href="#rfc.index.E">E</a> <a href="#rfc.index.F">F</a> <a href="#rfc.index.G">G</a> <a href="#rfc.index.H">H</a> <a href="#rfc.index.M">M</a> <a href="#rfc.index.N">N</a> <a href="#rfc.index.O">O</a> <a href="#rfc.index.P">P</a> <a href="#rfc.index.S">S</a> <a href="#rfc.index.V">V</a> <a href="#rfc.index.W">W</a> </p><div class="print2col"><ul class="ind"><li><a id="rfc.index.A" href="#rfc.index.A"><b>A</b></a><ul><li>age&nbsp;&nbsp;<a href="#rfc.iref.a.1">4.2</a></li><li>Age header field&nbsp;&nbsp;<a href="#rfc.section.5.1"><b>5.1</b></a></li></ul></li><li><a id="rfc.index.C" href="#rfc.index.C"><b>C</b></a><ul><li>cache&nbsp;&nbsp;<a href="#rfc.iref.c.1">1</a></li><li>cache key&nbsp;&nbsp;<a href="#rfc.section.2">2</a>, <a href="#rfc.iref.c.2">2</a></li><li>Cache-Control header field&nbsp;&nbsp;<a href="#rfc.section.5.2"><b>5.2</b></a></li><li>collapsed requests&nbsp;&nbsp;<a href="#rfc.iref.c.3">4</a></li></ul></li><li><a id="rfc.index.E" href="#rfc.index.E"><b>E</b></a><ul><li>Expires header field&nbsp;&nbsp;<a href="#rfc.section.5.3"><b>5.3</b></a></li><li>explicit expiration time&nbsp;&nbsp;<a href="#rfc.iref.e.1">4.2</a></li></ul></li><li><a id="rfc.index.F" href="#rfc.index.F"><b>F</b></a><ul><li>Fields&nbsp;&nbsp;<ul><li>Age&nbsp;&nbsp;<a href="#rfc.section.5.1"><b>5.1</b></a>, <a href="#rfc.section.5.1"><b>5.1</b></a></li><li>Cache-Control&nbsp;&nbsp;<a href="#rfc.section.5.2"><b>5.2</b></a></li><li>Expires&nbsp;&nbsp;<a href="#rfc.section.5.3"><b>5.3</b></a>, <a href="#rfc.section.5.3"><b>5.3</b></a></li><li>Pragma&nbsp;&nbsp;<a href="#rfc.section.5.4"><b>5.4</b></a>, <a href="#rfc.section.5.4"><b>5.4</b></a></li><li>Warning&nbsp;&nbsp;<a href="#rfc.section.5.5"><b>5.5</b></a></li></ul></li><li>fresh&nbsp;&nbsp;<a href="#rfc.section.4.2">4.2</a></li><li>freshness lifetime&nbsp;&nbsp;<a href="#rfc.iref.f.1">4.2</a></li></ul></li><li><a id="rfc.index.G" href="#rfc.index.G"><b>G</b></a><ul><li>Grammar&nbsp;&nbsp;<ul><li>Age&nbsp;&nbsp;<a href="#rfc.iref.g.2"><b>5.1</b></a></li><li>Cache-Control&nbsp;&nbsp;<a href="#rfc.iref.g.3"><b>5.2</b></a></li><li>cache-directive&nbsp;&nbsp;<a href="#rfc.iref.g.4"><b>5.2</b></a></li><li>delta-seconds&nbsp;&nbsp;<a href="#rfc.iref.g.1"><b>1.3</b></a></li><li>DIGIT&nbsp;&nbsp;<a href="#rfc.section.1.2"><b>1.2</b></a></li><li>Expires&nbsp;&nbsp;<a href="#rfc.iref.g.5"><b>5.3</b></a></li></ul></li></ul></li><li><a id="rfc.index.H" href="#rfc.index.H"><b>H</b></a><ul><li>Header Fields&nbsp;&nbsp;<ul><li>Age&nbsp;&nbsp;<a href="#rfc.section.5.1"><b>5.1</b></a>, <a href="#rfc.section.5.1"><b>5.1</b></a></li><li>Cache-Control&nbsp;&nbsp;<a href="#rfc.section.5.2"><b>5.2</b></a></li><li>Expires&nbsp;&nbsp;<a href="#rfc.section.5.3"><b>5.3</b></a>, <a href="#rfc.section.5.3"><b>5.3</b></a></li><li>Pragma&nbsp;&nbsp;<a href="#rfc.section.5.4"><b>5.4</b></a>, <a href="#rfc.section.5.4"><b>5.4</b></a></li><li>Warning&nbsp;&nbsp;<a href="#rfc.section.5.5"><b>5.5</b></a></li></ul></li><li>heuristic expiration time&nbsp;&nbsp;<a href="#rfc.iref.h.1">4.2</a></li><li>heuristically cacheable&nbsp;&nbsp;<a href="#rfc.section.4.2.2">4.2.2</a></li></ul></li><li><a id="rfc.index.M" href="#rfc.index.M"><b>M</b></a><ul><li>max-age (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.1"><b>5.2.1.1</b></a>, <a href="#rfc.section.5.2.2.1"><b>5.2.2.1</b></a></li><li>max-stale (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.2"><b>5.2.1.2</b></a></li><li>min-fresh (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.3"><b>5.2.1.3</b></a></li><li>must-revalidate (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.2.2"><b>5.2.2.2</b></a></li><li>must-understand (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.2.3"><b>5.2.2.3</b></a></li></ul></li><li><a id="rfc.index.N" href="#rfc.index.N"><b>N</b></a><ul><li>no-cache (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.4"><b>5.2.1.4</b></a>, <a href="#rfc.section.5.2.2.4"><b>5.2.2.4</b></a></li><li>no-store (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.5"><b>5.2.1.5</b></a>, <a href="#rfc.section.5.2.2.5"><b>5.2.2.5</b></a></li><li>no-transform (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.6"><b>5.2.1.6</b></a>, <a href="#rfc.section.5.2.2.6"><b>5.2.2.6</b></a></li></ul></li><li><a id="rfc.index.O" href="#rfc.index.O"><b>O</b></a><ul><li>only-if-cached (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.1.7"><b>5.2.1.7</b></a></li></ul></li><li><a id="rfc.index.P" href="#rfc.index.P"><b>P</b></a><ul><li>Pragma header field&nbsp;&nbsp;<a href="#rfc.section.5.4"><b>5.4</b></a></li><li>private (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.2.7"><b>5.2.2.7</b></a></li><li>private cache&nbsp;&nbsp;<a href="#rfc.iref.p.1">1</a></li><li>proxy-revalidate (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.2.8"><b>5.2.2.8</b></a></li><li>public (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.2.9"><b>5.2.2.9</b></a></li></ul></li><li><a id="rfc.index.S" href="#rfc.index.S"><b>S</b></a><ul><li>s-maxage (cache directive)&nbsp;&nbsp;<a href="#rfc.section.5.2.2.10"><b>5.2.2.10</b></a></li><li>selected response&nbsp;&nbsp;<a href="#rfc.iref.s.2">4.1</a></li><li>shared cache&nbsp;&nbsp;<a href="#rfc.iref.s.1">1</a></li><li>stale&nbsp;&nbsp;<a href="#rfc.section.4.2">4.2</a></li><li>strong validator&nbsp;&nbsp;<a href="#rfc.iref.s.3">4.3.4</a></li></ul></li><li><a id="rfc.index.V" href="#rfc.index.V"><b>V</b></a><ul><li>validator&nbsp;&nbsp;<a href="#rfc.section.4.3.1">4.3.1</a></li></ul></li><li><a id="rfc.index.W" href="#rfc.index.W"><b>W</b></a><ul><li>Warning header field&nbsp;&nbsp;<a href="#rfc.section.5.5"><b>5.5</b></a></li></ul></li></ul></div></section><section id="rfc.authors" class="avoidbreakinside"><h2><a href="#rfc.authors">Authors' Addresses</a></h2><address><b>Roy T. Fielding</b> (editor)<br>Adobe<br>345 Park Ave<br>San Jose, CA&nbsp;95110<br>United States of America<br>Email: fielding@gbiv.com<br>URI: <a href="https://roy.gbiv.com/">https://roy.gbiv.com/</a></address><address><b>Mark Nottingham</b> (editor)<br>Fastly<br>Prahran, VIC<br>Australia<br>Email: mnot@mnot.net<br>URI: <a href="https://www.mnot.net/">https://www.mnot.net/</a></address><address><b>Julian Reschke</b> (editor)<br>greenbytes GmbH<br>Hafenweg 16<br>Münster, 48155<br>Germany<br>Email: julian.reschke@greenbytes.de<br>URI: <a href="https://greenbytes.de/tech/webdav/">https://greenbytes.de/tech/webdav/</a></address></section></div></div></div><script src="/spec/script.mjs" type="module"></script></body></html>
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/rfc9111.xml b/test/fixtures/cache-tests/spec/rfc9111.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/rfc9111.xml
@@ -0,0 +1,2535 @@
+<?xml version="1.0" encoding="utf-8"?>
+<?xml-stylesheet type='text/xsl' href='lib/myxml2rfc.xslt'?>
+<!DOCTYPE rfc [
+  <!ENTITY MAY "<bcp14>MAY</bcp14>">
+  <!ENTITY MUST "<bcp14>MUST</bcp14>">
+  <!ENTITY MUST-NOT "<bcp14>MUST NOT</bcp14>">
+  <!ENTITY OPTIONAL "<bcp14>OPTIONAL</bcp14>">
+  <!ENTITY RECOMMENDED "<bcp14>RECOMMENDED</bcp14>">
+  <!ENTITY REQUIRED "<bcp14>REQUIRED</bcp14>">
+  <!ENTITY SHALL "<bcp14>SHALL</bcp14>">
+  <!ENTITY SHALL-NOT "<bcp14>SHALL NOT</bcp14>">
+  <!ENTITY SHOULD "<bcp14>SHOULD</bcp14>">
+  <!ENTITY SHOULD-NOT "<bcp14>SHOULD NOT</bcp14>">
+  <!ENTITY ID-VERSION "latest">
+  <!ENTITY mdash "&#8212;">
+  <!ENTITY uuml "&#252;">
+  <!ENTITY Note "<x:h xmlns:x='http://purl.org/net/xml2rfc/ext'>Note:</x:h>">
+]>
+<?rfc toc="yes" ?>
+<?rfc tocdepth="4" ?>
+<?rfc symrefs="yes" ?>
+<?rfc sortrefs="yes" ?>
+<?rfc compact="yes"?>
+<?rfc subcompact="no" ?>
+<?rfc linkmailto="no" ?>
+<?rfc editing="no" ?>
+<?rfc comments="yes"?>
+<?rfc inline="yes"?>
+<?rfc rfcedstyle="yes"?>
+<?rfc-ext allow-markup-in-artwork="yes" ?>
+<?rfc-ext html-pretty-print="prettyprint https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"?>
+<?rfc-ext include-references-in-index="yes" ?>
+<?rfc-ext xml2rfc-backend="202007"?>
+<?github-issue-label cache?>
+
+<rfc category="std" docName="draft-ietf-httpbis-cache-&ID-VERSION;" ipr="pre5378Trust200902"
+  obsoletes="7234" x:maturity-level="proposed" xmlns:x="http://purl.org/net/xml2rfc/ext" xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
+  tocDepth="4">
+<x:feedback template="mailto:ietf-http-wg@w3.org?subject={docname},%20%22{section}%22&amp;body=&lt;{ref}&gt;:"/>
+<front>
+
+  <title>HTTP Caching</title>
+
+  <author fullname="Roy T. Fielding" initials="R." surname="Fielding" role="editor">
+    <organization>Adobe</organization>
+    <address>
+      <postal>
+        <street>345 Park Ave</street>
+        <city>San Jose</city>
+        <region>CA</region>
+        <code>95110</code>
+        <country>United States of America</country>
+      </postal>
+      <email>fielding@gbiv.com</email>
+      <uri>https://roy.gbiv.com/</uri>
+    </address>
+  </author>
+
+  <author fullname="Mark Nottingham" initials="M." surname="Nottingham" role="editor">
+    <organization>Fastly</organization>
+    <address>
+       <postal>
+         <city>Prahran</city>
+         <region>VIC</region>
+         <country>Australia</country>
+       </postal>
+      <email>mnot@mnot.net</email>
+      <uri>https://www.mnot.net/</uri>
+    </address>
+  </author>
+
+  <author fullname="Julian Reschke" initials="J." surname="Reschke" role="editor">
+    <organization abbrev="greenbytes">greenbytes GmbH</organization>
+    <address>
+      <postal>
+        <street>Hafenweg 16</street>
+        <city>M&uuml;nster</city><code>48155</code>
+        <country>Germany</country>
+      </postal>
+      <email>julian.reschke@greenbytes.de</email>
+      <uri>https://greenbytes.de/tech/webdav/</uri>
+    </address>
+  </author>
+
+  <date/>
+
+  <area>Applications and Real-Time</area>
+  <workgroup>HTTP</workgroup>
+
+  <keyword>Hypertext Transfer Protocol</keyword>
+  <keyword>HTTP</keyword>
+  <keyword>HTTP Caching</keyword>
+
+<abstract>
+<t>
+   The Hypertext Transfer Protocol (HTTP) is a stateless application-level
+   protocol for distributed, collaborative, hypertext information systems.
+   This document defines HTTP caches and the associated header fields that
+   control cache behavior or indicate cacheable response messages.
+</t>
+<t>
+   This document obsoletes RFC 7234.
+</t>
+</abstract>
+
+<note title="Editorial Note" removeInRFC="true">
+  <t>
+    Discussion of this draft takes place on the HTTP working group
+    mailing list (ietf-http-wg@w3.org), which is archived at
+    <eref target="https://lists.w3.org/Archives/Public/ietf-http-wg/"/>.
+  </t>
+  <t>
+    Working Group information can be found at <eref target="https://httpwg.org/"/>;
+    source code and issues list for this draft can be found at
+    <eref target="https://github.com/httpwg/http-core"/>.
+  </t>
+  <t>
+    The changes in this draft are summarized in <xref target="changes.since.14"/>.
+  </t>
+</note>
+
+   </front>
+   <middle>
+
+<section title="Introduction" anchor="caching">
+<t>
+   The Hypertext Transfer Protocol (HTTP) is a stateless application-level
+   request/response protocol that uses extensible semantics and
+   self-descriptive messages for flexible interaction with network-based
+   hypertext information systems. It is typically used for distributed information systems, where
+   the use of response caches can improve performance. This document
+   defines aspects of HTTP related to caching and reusing response
+   messages.
+</t>
+
+<iref item="cache" />
+<t>
+   An HTTP <x:dfn>cache</x:dfn> is a local store of response messages and the
+   subsystem that controls storage, retrieval, and deletion of messages in it.
+   A cache stores cacheable responses to reduce the response time and
+   network bandwidth consumption on future equivalent requests. Any client or
+   server &MAY; use a cache, though not when acting as a tunnel.
+</t>
+<iref item="shared cache" />
+<iref item="private cache" />
+<t anchor="shared.and.private.caches">
+   A <x:dfn>shared cache</x:dfn> is a cache that stores responses for reuse
+   by more than one user; shared caches are usually (but not always) deployed
+   as a part of an intermediary. A <x:dfn>private cache</x:dfn>, in contrast,
+   is dedicated to a single user; often, they are deployed as a component of
+   a user agent.
+</t>
+<t>
+   HTTP caching's goal is significantly improving performance
+   by reusing a prior response message to satisfy a current request.
+   A cache considers a stored response "fresh", as defined in
+   <xref target="expiration.model" />, if it can be reused without
+   "validation" (checking with the origin server to see if the cached response
+   remains valid for this request).  A fresh response can therefore
+   reduce both latency and network overhead each time the cache reuses it.
+   When a cached response is not fresh, it might still be reusable if validation
+   can freshen it (<xref target="validation.model" />) or if the
+   origin is unavailable (<xref target="serving.stale.responses" />).
+</t>
+<t>
+   This document obsoletes <xref target="RFC7234" format="none">RFC 7234</xref>,
+   with the changes being summarized in <xref target="changes.from.rfc.7234"/>.
+</t>
+
+<section title="Requirements Notation" anchor="requirements.notation">
+<t>
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
+   NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
+   "MAY", and "OPTIONAL" in this document are to be interpreted as
+   described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
+   appear in all capitals, as shown here.
+</t>
+<t>
+   <xref target="Semantics-conformance"/> defines conformance criteria and contains considerations regarding error handling.
+</t>
+</section>
+
+<section title="Syntax Notation" anchor="notation">
+<iref primary="true" item="Grammar" subitem="DIGIT"/>
+<t>
+   This specification uses the Augmented Backus-Naur Form (ABNF) notation of
+   <xref target="RFC5234"/>, extended with the notation for case-sensitivity
+   in strings defined in <xref target="RFC7405"/>.
+</t>
+<t>
+   It also uses a list extension, defined in <xref target="abnf.extension"/>,
+   that allows for compact definition of comma-separated lists using a '#'
+   operator (similar to how the '*' operator indicates repetition). <xref
+   target="collected.abnf"/> shows the collected grammar with all list
+   operators expanded to standard ABNF notation.
+</t>
+<t anchor="core.rules">
+  <x:anchor-alias value="DIGIT"/>
+   The following core rule is included by
+   reference, as defined in <xref target="RFC5234" x:fmt="," x:sec="B.1"/>:
+   DIGIT (decimal 0-9).
+</t>
+<t anchor="imported.rules">
+  <x:anchor-alias value="HTTP-date"/>
+  <x:anchor-alias value="OWS"/>
+  <x:anchor-alias value="field-name"/>
+  <x:anchor-alias value="quoted-string"/>
+  <x:anchor-alias value="token"/>
+  <xref target="Semantics"/> defines the following rules:
+</t>
+<sourcecode type="abnf7230">
+  <x:ref>HTTP-date</x:ref>     = &lt;HTTP-date, see <xref target="http.date"/>&gt;
+  <x:ref>OWS</x:ref>           = &lt;OWS, see <xref target="whitespace"/>&gt;
+  <x:ref>field-name</x:ref>    = &lt;field-name, see <xref target="field-names"/>&gt;
+  <x:ref>quoted-string</x:ref> = &lt;quoted-string, see <xref target="quoted.strings"/>&gt;
+  <x:ref>token</x:ref>         = &lt;token, see <xref target="tokens"/>&gt;
+</sourcecode>
+</section>
+
+<section title="Delta Seconds" anchor="delta-seconds">
+<t>
+   The delta-seconds rule specifies a non-negative integer, representing time
+   in seconds.
+</t>
+<sourcecode type="abnf7230"><iref item="Grammar" primary="true" subitem="delta-seconds" />
+  <x:ref>delta-seconds</x:ref>  = 1*<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   A recipient parsing a delta-seconds value and converting it to binary form
+   ought to use an arithmetic type of at least 31 bits of non-negative integer
+   range.
+   If a cache receives a delta-seconds value greater than the greatest integer
+   it can represent, or if any of its subsequent calculations overflows,
+   the cache &MUST; consider the value to be 2147483648
+   (2<sup>31</sup>) or the greatest positive integer it can conveniently
+   represent.
+</t>
+<aside>
+   <t>
+       &Note; The value 2147483648 is here for historical reasons,
+       represents infinity (over 68 years), and does not need to be stored in
+       binary form; an implementation could produce it as a canned string if
+       any overflow occurs, even if the calculations are performed with an
+       arithmetic type incapable of directly representing that number.
+       What matters here is that an overflow be detected and not treated as a
+       negative value in later calculations.
+   </t>
+</aside>
+</section>
+</section>
+
+<section title="Overview of Cache Operation" anchor="caching.overview">
+<iref item="cache key" />
+<t>
+   Proper cache operation preserves the semantics of HTTP transfers (<xref
+   target="Semantics"/>) while reducing the transmission of information already
+   held in the cache. Although caching is an entirely &OPTIONAL; feature of
+   HTTP, it can be assumed that reusing a cached response is desirable and
+   that such reuse is the default behavior when no requirement or local
+   configuration prevents it. Therefore, HTTP cache requirements are focused
+   on preventing a cache from either storing a non-reusable response or
+   reusing a stored response inappropriately, rather than mandating that
+   caches always store and reuse particular responses.
+</t>
+<iref item="cache key" />
+<t>
+   The <x:dfn>cache key</x:dfn> is the information a cache uses to select a response and
+   is comprised of, at a minimum, the request method and target
+   URI used to retrieve the stored response; the method determines under which
+   circumstances that response can be used to satisfy a subsequent request. However, many
+   HTTP caches in common use today only cache GET responses, and therefore only
+   use the URI as the cache key, forwarding other methods.
+</t>
+<t>
+   If a request target is subject to content negotiation, the cache might
+   store multiple responses for it. Caches differentiate these responses
+   by incorporating values of the original request's selecting header fields
+   into the cache key as well, using information in the <x:ref>Vary</x:ref>
+   response header field, as per <xref target="caching.negotiated.responses"/>.
+</t>
+<t>
+  Caches might incorporate additional material into the cache key.
+  For example, user agent caches might include the referring site's identity,
+  thereby "double keying" the cache to avoid some privacy risks (see <xref
+  target="security.timing"/>).
+</t>
+<t>
+   Most commonly, caches store the successful result of a retrieval
+   request: i.e., a <x:ref>200 (OK)</x:ref> response to a GET request, which
+   contains a representation of the target resource
+   (<xref target="GET"/>). However, it is also possible to store
+   redirects, negative results (e.g., <x:ref>404 (Not Found)</x:ref>),
+   incomplete results (e.g., <x:ref>206 (Partial Content)</x:ref>), and
+   responses to methods other than GET if the method's definition allows such
+   caching and defines something suitable for use as a cache key.
+</t>
+<t>
+   A cache is <x:dfn>disconnected</x:dfn> when it cannot contact the origin
+   server or otherwise find a forward path for a request. A
+   disconnected cache can serve stale responses in some circumstances (<xref
+   target="serving.stale.responses"/>).
+</t>
+
+</section>
+
+<section title="Storing Responses in Caches" anchor="response.cacheability">
+<t>
+   A cache &MUST-NOT; store a response to a request unless:
+</t>
+<ul>
+  <li><t>the request method is understood by the cache;</t></li>
+  <li><t>the response status code is final (see
+         <xref target="status.codes"/>);</t></li>
+  <li><t>if the response status code is 206 or 304, or the "must-understand" cache directive (see <xref
+         target="cache-response-directive.must-understand"/>) is present: the cache understands the response status code;</t></li>
+  <li><t>the "no-store" cache directive is not present in the response
+         (see <xref target="cache-response-directive.no-store"/>);</t></li>
+  <li><t>if the cache is shared: the "private" response directive is either not
+         present or allows a shared cache to store a modified response;
+         see <xref target="cache-response-directive.private" />);</t></li>
+  <li><t>if the cache is shared: the <x:ref>Authorization</x:ref> header field
+         is not present in the request
+         (see <xref target="field.authorization"/>) or a
+         response directive is present that explicitly allows shared caching
+         (see <xref target="caching.authenticated.responses" />);
+         and,</t></li>
+  <li><t>the response contains at least one of:</t>
+     <ul>
+        <li>a public response directive
+            (see <xref target="cache-response-directive.public"/>);</li>
+        <li>a private response directive, if the cache is not shared
+            (see <xref target="cache-response-directive.private"/>);</li>
+        <li>an <x:ref>Expires</x:ref> header field
+            (see <xref target="field.expires"/>);</li>
+        <li>a max-age response directive
+            (see <xref target="cache-response-directive.max-age" />);</li>
+        <li>if the cache is shared: an s-maxage response directive
+            (see <xref target="cache-response-directive.s-maxage" />);</li>
+        <li>a Cache Control Extension that allows it to be cached
+            (see <xref target="cache.control.extensions" />); or,</li>
+        <li>a status code that is defined as heuristically cacheable
+            (see <xref target="heuristic.freshness" />).</li>
+     </ul>
+  </li>
+</ul>
+<t>
+   Note that a cache-control extension can override any of the requirements
+   listed; see <xref target="cache.control.extensions" />.
+</t>
+<t>
+   In this context, a cache has "understood" a request method or a response
+   status code if it recognizes it and implements all specified
+   caching-related behavior.
+</t>
+<t>
+   Note that, in normal operation, some caches will not store a response that
+   has neither a cache validator nor an explicit expiration time, as such
+   responses are not usually useful to store. However, caches are not
+   prohibited from storing such responses.
+</t>
+
+<section title="Storing Header and Trailer Fields" anchor="storing.fields">
+<t>
+   Caches &MUST; include all received response header fields &mdash; including
+   unrecognised ones &mdash; when storing a response; this assures that new HTTP
+   header fields can be successfully deployed. However, the following exceptions
+   are made:
+</t>
+<ul>
+   <li>The Connection header field and fields whose names are listed in it are
+   required by <xref target="field.connection"/> to be removed before
+   forwarding the message. This &MAY; be implemented by doing so
+   before storage.</li>
+   <li>Likewise, some fields' semantics require them to be removed
+   before forwarding the message, and this &MAY; be implemented by doing so
+   before storage; see <xref target="field.connection"/> for some
+   examples.</li>
+   <li>The no-cache (<xref target="cache-response-directive.no-cache"/>) and
+   private (<xref target="cache-response-directive.private"/>) cache
+   directives can have arguments that prevent storage of header fields by all
+   caches and shared caches, respectively.</li>
+   <li>Header fields that are specific to a client's proxy configuration
+   &MUST-NOT; be stored, unless the cache incorporates the identity of the
+   proxy into the cache key. Effectively, this is limited to Proxy-Authenticate
+   (<xref target="field.proxy-authenticate"/>), Proxy-Authentication-Info
+   (<xref target="field.proxy-authentication-info"/>), and Proxy-Authorization
+   (<xref target="field.proxy-authorization"/>).</li>
+</ul>
+<t>
+   Caches &MAY; either store trailer fields separate from header fields, or
+   discard them. Caches &MUST-NOT; combine trailer fields with header fields.
+</t>
+</section>
+
+<section title="Updating Stored Header Fields" anchor="update">
+<t>
+   Caches are required to update a stored response's header fields from another
+   (typically newer) response in several situations; for example, see <xref
+   target="combining.responses"/>, <xref target="freshening.responses"/> and
+   <xref target="head.effects"/>.
+</t>
+<t>
+   When doing so, the cache &MUST; add each header field in the provided response
+   to the stored response, replacing field values that are already present,
+   with the following exceptions:
+</t>
+<ul>
+   <li>Header fields excepted from storage in <xref target="storing.fields"/>,</li>
+   <li>Header fields that the cache's stored response depends upon, as described below,</li>
+   <li>Header fields that are automatically processed and removed by the recipient, as described below, and</li>
+   <li>The <x:ref>Content-Length</x:ref> header field.</li>
+</ul>
+<t>
+   In some cases, caches (especially in user agents) store the results of
+   processing the received response, rather than the response itself,
+   and updating header fields that affect that processing can result in
+   inconsistent behavior and security issues. Caches in this situation &MAY;
+   omit these header fields from updating stored responses on an
+   exceptional basis, but &SHOULD; limit such omission to those fields
+   necessary to assure integrity of the stored response.
+</t>
+<t>
+   For example, a browser might decode the content coding of a response
+   while it is being received, creating a disconnect between the data it has
+   stored and the response's original metadata.
+   Updating that stored metadata with a different <x:ref>Content-Encoding</x:ref>
+   header field would be problematic. Likewise, a browser might store a
+   post-parse HTML tree, rather than the content received in
+   the response; updating the <x:ref>Content-Type</x:ref> header field would not be workable
+   in this case, because any assumptions about the format made in parsing would
+   now be invalid.
+</t>
+<t>
+   Furthermore, some fields are automatically processed and removed by the
+   HTTP implementation; for example, the <x:ref>Content-Range</x:ref> header field.
+   Implementations &MAY; automatically omit such header fields from updates,
+   even when the processing does not actually occur.
+</t>
+<t>
+   Note that the Content-* prefix is not a signal that a header field is omitted
+   from update; it is a convention for MIME header fields, not HTTP.
+</t>
+</section>
+
+<section title="Storing Incomplete Responses" anchor="incomplete.responses">
+<t>
+   If the request method is GET, the response status code is <x:ref>200
+   (OK)</x:ref>, and the entire response header section has been received, a
+   cache &MAY; store a response body that is not complete (<xref target="messages"/>) if the stored response
+   is recorded as being incomplete. Likewise, a <x:ref>206 (Partial
+   Content)</x:ref> response &MAY; be stored as if it were an incomplete
+   <x:ref>200 (OK)</x:ref> response. However, a cache &MUST-NOT; store
+   incomplete or partial-content responses if it does not support the
+   <x:ref>Range</x:ref> and <x:ref>Content-Range</x:ref> header fields or if
+   it does not understand the range units used in those fields.
+</t>
+<t>
+   A cache &MAY; complete a stored incomplete response by making a subsequent
+   range request (<xref target="field.range"/>) and combining the successful response with the
+   stored response, as defined in <xref target="combining.responses"/>. A cache
+   &MUST-NOT; use an incomplete response to answer requests unless the
+   response has been made complete, or the request is partial and specifies a
+   range wholly within the incomplete response. A cache &MUST-NOT;
+   send a partial response to a client without explicitly marking it
+   using the <x:ref>206 (Partial Content)</x:ref> status code.
+</t>
+</section>
+
+<section title="Combining Partial Content" anchor="combining.responses">
+<t>
+   A response might transfer only a partial representation if the
+   connection closed prematurely or if the request used one or more Range
+   specifiers (<xref target="field.range"/>).  After several such transfers, a cache might have
+   received several ranges of the same representation.  A cache &MAY; combine
+   these ranges into a single stored response, and reuse that response to
+   satisfy later requests, if they all share the same strong validator and
+   the cache complies with the client requirements in <xref target="combining.byte.ranges"/>.
+</t>
+<t>
+   When combining the new response with one or more stored responses, a cache
+   &MUST; update the stored response header fields using the header fields
+   provided in the new response, as per <xref target="update"/>.
+</t>
+</section>
+
+<section title="Storing Responses to Authenticated Requests" anchor="caching.authenticated.responses">
+<t>
+   A shared cache &MUST-NOT; use a cached response to a request with an
+   <x:ref>Authorization</x:ref> header field (<xref target="field.authorization"/>) to
+   satisfy any subsequent request unless the response contains a
+   <x:ref>Cache-Control</x:ref> field with a response directive
+   (<xref target="cache-response-directive"/>) that allows it to be stored by
+   a shared cache and the cache conforms to the requirements of that
+   directive for that response.
+</t>
+<t>
+   In this specification, the following response directives have such an effect:
+   must-revalidate (<xref target="cache-response-directive.must-revalidate"/>),
+   public (<xref target="cache-response-directive.public"/>), and
+   s-maxage (<xref target="cache-response-directive.s-maxage"/>).
+</t>
+</section>
+
+</section>
+
+
+<section title="Constructing Responses from Caches" anchor="constructing.responses.from.caches">
+<t>
+   When presented with a request, a cache &MUST-NOT; reuse a stored response,
+   unless:
+</t>
+<ul>
+   <li><t>The presented target URI (<xref target="target.resource"/>) and
+   that of the stored response match, and</t></li>
+   <li><t>the request method associated with the stored response allows it to
+   be used for the presented request, and</t></li>
+   <li><t>selecting header fields nominated by the stored response (if any)
+   match those presented (see <xref target="caching.negotiated.responses"
+   />), and</t></li>
+   <li><t>the stored response does not contain the no-cache cache directive
+   (<xref target="cache-response-directive.no-cache"/>), unless it is
+   successfully validated (<xref target="validation.model"/>), and</t></li>
+   <li><t>the stored response is either:</t>
+     <ul>
+        <li>fresh (see <xref target="expiration.model" />), or</li>
+        <li>allowed to be served stale (see <xref
+        target="serving.stale.responses" />), or</li>
+        <li>successfully validated (see <xref target="validation.model"
+        />).</li>
+     </ul>
+   </li>
+</ul>
+<t>
+   Note that a cache-control extension can override any of the requirements
+   listed; see <xref target="cache.control.extensions" />.
+</t>
+<t>
+   When a stored response is used to satisfy a request without validation, a
+   cache &MUST; generate an <x:ref>Age</x:ref> header field (<xref
+   target="field.age"/>), replacing any present in the response with a value
+   equal to the stored response's current_age; see <xref
+   target="age.calculations" />.
+</t>
+<t>
+   A cache &MUST; write through requests with methods that are unsafe
+   (<xref target="safe.methods"/>) to the origin server; i.e., a cache is not allowed to
+   generate a reply to such a request before having forwarded the request and
+   having received a corresponding response.
+</t>
+<t>
+   Also, note that unsafe requests might invalidate already-stored responses;
+   see <xref target="invalidation" />.
+</t>
+<iref item="collapsed requests" />
+<t>
+   A response that is stored or storable can be used to satisfy multiple
+   requests, provided that it is allowed to reuse that response for the requests
+   in question. This enables caches to <x:dfn>collapse requests</x:dfn> &mdash; or combine multiple incoming requests into a single forward
+   one upon a cache miss &mdash; thereby reducing load on the origin server and network.
+   However, note that if the response returned is not able to be used for some or all
+   of the collapsed requests, additional latency might be introduced, because they will
+   need to be forwarded to be satisfied.
+</t>
+<t>
+   When more than one suitable response is stored, a cache &MUST; use the
+   most recent one (as determined by the <x:ref>Date</x:ref> header
+   field). It can also forward the request with "Cache-Control: max-age=0" or
+   "Cache-Control: no-cache" to disambiguate which response to use.
+</t>
+<t>
+   A cache that does not have a clock available &MUST-NOT; use stored
+   responses without revalidating them upon every use.
+</t>
+
+<section title="Calculating Cache Keys with Vary" anchor="caching.negotiated.responses">
+<t>
+   When a cache receives a request that can be satisfied by a stored response
+   that has a <x:ref>Vary</x:ref> header field (<xref target="field.vary"/>),
+   it &MUST-NOT; use that response unless all the selecting header fields
+   nominated by the Vary header field match in both the original request
+   (i.e., that associated with the stored response), and the presented
+   request.
+</t>
+<t>
+   The selecting header fields from two requests are defined to match if
+   and only if those in the first request can be transformed to those in the
+   second request by applying any of:
+</t>
+<ul>
+  <li>
+     adding or removing whitespace, where allowed in the header field's
+     syntax
+  </li>
+  <li>
+     combining multiple header field lines with the same field name
+     (see <xref target="field.lines"/>)
+  </li>
+  <li>
+     normalizing both header field values in a way that is known to have
+     identical semantics, according to the header field's specification
+     (e.g., reordering field values when order is not significant;
+     case-normalization, where values are defined to be case-insensitive)
+  </li>
+</ul>
+<t>
+   If (after any normalization that might take place) a header field is absent
+   from a request, it can only match another request if it is also absent
+   there.
+</t>
+<t>
+   A <x:ref>Vary</x:ref> header field value containing a member "*" always fails to match.
+</t>
+<iref item="selected response" />
+<t>
+   The stored response with matching selecting header fields is known as the
+   <x:dfn>selected response</x:dfn>.
+</t>
+<t>
+   If multiple selected responses are available (potentially including
+   responses without a Vary header field), the cache will need to choose one to use.
+   When a selecting header field has a known mechanism for doing so (e.g., qvalues on
+   <x:ref>Accept</x:ref> and similar request header fields), that mechanism &MAY; be
+   used to select a preferred response. If such a mechanism is not available, or leads to equally preferred responses, the most recent
+   response (as determined by the <x:ref>Date</x:ref> header field) is used, as
+   per <xref target="constructing.responses.from.caches"/>.
+</t>
+<t>
+   Some resources mistakenly omit the Vary header field from their default
+   response (i.e., the one sent when no more preferable response is available),
+   with the effect of selecting it for requests to that resource even when
+   more preferable responses are available. When a cache has multiple responses for a
+   target URI and one or more omits the Vary header field, it &SHOULD; use the
+   most recent (see <xref target="age.calculations"/>) valid Vary field value available to select an appropriate response
+   for the request.
+</t>
+<t>
+   If no selected response is available, the cache cannot satisfy the
+   presented request. Typically, it is forwarded to the origin server
+   in a (possibly conditional; see <xref target="validation.model"/>) request.
+</t>
+</section>
+
+<section title="Freshness" anchor="expiration.model">
+<iref item="fresh" />
+<iref item="stale" />
+<t>
+   A <x:dfn>fresh</x:dfn> response is one whose age has not yet exceeded its
+   freshness lifetime. Conversely, a <x:dfn>stale</x:dfn>
+   response is one where it has.
+</t>
+<iref item="freshness lifetime" />
+<iref item="explicit expiration time" />
+<iref item="heuristic expiration time" />
+<t>
+   A response's <x:dfn>freshness lifetime</x:dfn> is the length of time
+   between its generation by the origin server and its expiration time. An
+   <x:dfn>explicit expiration time</x:dfn> is the time at which the origin
+   server intends that a stored response can no longer be used by a cache
+   without further validation, whereas a <x:dfn>heuristic expiration
+   time</x:dfn> is assigned by a cache when no explicit expiration time is
+   available.
+</t>
+<iref item="age" />
+<t>
+   A response's <x:dfn>age</x:dfn> is the time that has passed since it was
+   generated by, or successfully validated with, the origin server.
+</t>
+<t>
+   When a response is fresh, it can be used to satisfy
+   subsequent requests without contacting the origin server, thereby improving
+   efficiency.
+</t>
+<t>
+   The primary mechanism for determining freshness is for an origin server to
+   provide an explicit expiration time in the future, using either the
+   <x:ref>Expires</x:ref> header field (<xref target="field.expires" />) or
+   the max-age response directive (<xref
+   target="cache-response-directive.max-age" />). Generally, origin servers
+   will assign future explicit expiration times to responses in the belief
+   that the representation is not likely to change in a semantically
+   significant way before the expiration time is reached.
+</t>
+<t>
+   If an origin server wishes to force a cache to validate every request, it
+   can assign an explicit expiration time in the past to indicate that the
+   response is already stale. Compliant caches will normally validate a stale
+   cached response before reusing it for subsequent requests (see <xref
+   target="serving.stale.responses" />).
+</t>
+<t>
+   Since origin servers do not always provide explicit expiration times,
+   caches are also allowed to use a heuristic to determine an expiration time
+   under certain circumstances (see <xref target="heuristic.freshness"/>).
+</t>
+<t>
+  The calculation to determine if a response is fresh is:
+</t>
+<artwork type="code">
+   response_is_fresh = (freshness_lifetime &gt; current_age)
+</artwork>
+<t>
+   freshness_lifetime is defined in <xref
+   target="calculating.freshness.lifetime" />; current_age is defined in
+   <xref target="age.calculations" />.
+</t>
+<t>
+   Clients can send the max-age or min-fresh request directives (<xref
+   target="cache-request-directive" />) to constrain or relax freshness
+   calculations for the corresponding response. However, caches are not
+   required to honor them.
+</t>
+<t>
+   When calculating freshness, to avoid common problems in date parsing:
+</t>
+<ul>
+   <li>Although all date formats are specified to be case-sensitive,
+      a cache recipient &SHOULD; match the field value
+      case-insensitively.</li>
+   <li>If a cache recipient's internal implementation of time has less
+      resolution than the value of an HTTP-date, the recipient &MUST;
+      internally represent a parsed <x:ref>Expires</x:ref> date as the
+      nearest time equal to or earlier than the received value.</li>
+   <li>A cache recipient &MUST-NOT; allow local time zones to influence the
+      calculation or comparison of an age or expiration time.</li>
+   <li>A cache recipient &SHOULD; consider a date with a zone abbreviation
+      other than "GMT" to be invalid for calculating expiration.</li>
+</ul>
+<t>
+   Note that freshness applies only to cache operation; it cannot be used to
+   force a user agent to refresh its display or reload a resource. See <xref
+   target="history.lists" /> for an explanation of the difference between
+   caches and history mechanisms.
+</t>
+
+<section title="Calculating Freshness Lifetime" anchor="calculating.freshness.lifetime">
+<t>
+   A cache can calculate the freshness lifetime (denoted as
+   freshness_lifetime) of a response by using the first match of:
+</t>
+<ul>
+   <li>If the cache is shared and the s-maxage response directive
+   (<xref target="cache-response-directive.s-maxage" />) is present, use its value,
+   or</li>
+   <li>If the max-age response directive (<xref
+   target="cache-response-directive.max-age" />) is present, use its value, or</li>
+   <li>If the <x:ref>Expires</x:ref> response header field
+   (<xref target="field.expires" />) is present, use its value minus the
+    value of the <x:ref>Date</x:ref> response header field
+    (using the time the message was received if it is not present, as per <xref target="field.date"/>), or</li>
+   <li>Otherwise, no explicit expiration time is present in the response. A
+   heuristic freshness lifetime might be applicable; see <xref
+   target="heuristic.freshness" />.</li>
+</ul>
+<t>
+   Note that this calculation is not vulnerable to clock skew, since all of
+   the information comes from the origin server.
+</t>
+<t>
+   When there is more than one value present for a given directive (e.g., two
+   <x:ref>Expires</x:ref> header field lines or multiple Cache-Control: max-age
+   directives), either the first occurrence should be used, or the response should
+   be considered stale. If directives conflict (e.g.,
+   both max-age and no-cache are present), the most restrictive directive should
+   be honored. Caches are encouraged to consider responses that have
+   invalid freshness information (e.g., a max-age directive with non-integer content) to
+   be stale.
+</t>
+</section>
+
+<section title="Calculating Heuristic Freshness" anchor="heuristic.freshness">
+  <iref item="heuristically cacheable"/>
+<t>
+   Since origin servers do not always provide explicit expiration times, a
+   cache &MAY; assign a heuristic expiration time when an explicit time is not
+   specified, employing algorithms that use other field values (such as
+   the <x:ref>Last-Modified</x:ref> time) to estimate a plausible expiration
+   time. This specification does not provide specific algorithms, but does
+   impose worst-case constraints on their results.
+</t>
+<t>
+   A cache &MUST-NOT; use heuristics to determine freshness when an explicit
+   expiration time is present in the stored response. Because of the
+   requirements in <xref target="response.cacheability"/>, this means that
+   heuristics can only be used on responses without explicit
+   freshness whose status codes are defined as <x:dfn>heuristically cacheable</x:dfn> (e.g., see
+   <xref target="overview.of.status.codes"/>), and those responses without
+   explicit freshness that have been marked as explicitly cacheable (e.g.,
+   with a "public" response directive).
+</t>
+<t>
+   Note that in previous specifications heuristically cacheable response status
+   codes were called "cacheable by default."
+</t>
+<t>
+   If the response has a <x:ref>Last-Modified</x:ref> header field
+   (<xref target="field.last-modified"/>), caches are encouraged to use a heuristic
+   expiration value that is no more than some fraction of the interval since
+   that time. A typical setting of this fraction might be 10%.
+</t>
+<aside>
+   <t>
+      &Note; <xref target="RFC2616" x:fmt="of" x:sec="13.9"/> prohibited caches
+      from calculating heuristic freshness for URIs with query components
+      (i.e., those containing '?'). In practice, this has not been widely
+      implemented. Therefore, origin servers are encouraged to send explicit
+      directives (e.g., Cache-Control: no-cache) if they wish to prevent
+      caching.
+   </t>
+</aside>
+</section>
+
+<section title="Calculating Age" anchor="age.calculations">
+<t>
+   The <x:ref>Age</x:ref> header field is used to convey an estimated
+   age of the response message when obtained from a cache. The Age field value
+   is the cache's estimate of the number of seconds since the origin server generated
+   or validated the response. The Age value is therefore
+   the sum of the time that the response has been resident in each of the
+   caches along the path from the origin server, plus the time it
+   has been in transit along network paths.
+</t>
+<t>
+   Age calculation uses the following data:
+</t>
+<dl newline="false">
+  <dt><x:dfn>age_value</x:dfn></dt>
+  <dd>
+     The term "age_value" denotes the value of the <x:ref>Age</x:ref>
+     header field (<xref target="field.age"/>), in a form appropriate for
+     arithmetic operation; or 0, if not available.
+  </dd>
+  <dt><x:dfn>date_value</x:dfn></dt>
+  <dd>
+     The term "date_value" denotes the value of
+     the Date header field, in a form appropriate for arithmetic
+     operations. See <xref target="field.date"/> for the definition of the Date header
+     field, and for requirements regarding responses without it.
+  </dd>
+  <dt><x:dfn>now</x:dfn></dt>
+  <dd>
+     The term "now" means "the current value of the clock at the host
+     performing the calculation". A host ought to use NTP (<xref
+     target="RFC5905"/>) or some similar protocol to synchronize its
+     clocks to Coordinated Universal Time.
+  </dd>
+  <dt><x:dfn>request_time</x:dfn></dt>
+  <dd>
+     The current value of the clock at the host at the time the request
+     resulting in the stored response was made.
+  </dd>
+  <dt><x:dfn>response_time</x:dfn></dt>
+  <dd>
+     The current value of the clock at the host at the time the response
+     was received.
+  </dd>
+</dl>
+<t>
+   A response's age can be calculated in two entirely independent ways:
+</t>
+<ol>
+   <li>the "apparent_age": response_time minus date_value, if the local
+   clock is reasonably well synchronized to the origin server's clock. If
+   the result is negative, the result is replaced by zero.</li>
+   <li>the "corrected_age_value", if all of the caches along the response
+   path implement HTTP/1.1 or greater. A cache &MUST; interpret this value
+   relative to the time the request was initiated, not the time that the
+   response was received.</li>
+</ol>
+<artwork type="code">
+  apparent_age = max(0, response_time - date_value);
+
+  response_delay = response_time - request_time;
+  corrected_age_value = age_value + response_delay;
+</artwork>
+<t>
+   The corrected_age_value &MAY; be used as the corrected_initial_age. In
+   circumstances where very old cache implementations that might not correctly
+   insert <x:ref>Age</x:ref> are present, corrected_initial_age can be calculated
+   more conservatively as
+</t>
+<artwork type="code">
+  corrected_initial_age = max(apparent_age, corrected_age_value);
+</artwork>
+<t>
+   The current_age of a stored response can then be calculated by adding the
+   time (in seconds) since the stored response was last validated by
+   the origin server to the corrected_initial_age.
+</t>
+<artwork type="code">
+  resident_time = now - response_time;
+  current_age = corrected_initial_age + resident_time;
+</artwork>
+</section>
+
+<section title="Serving Stale Responses" anchor="serving.stale.responses">
+<t>
+   A "stale" response is one that either has explicit expiry information or is
+   allowed to have heuristic expiry calculated, but is not fresh according to
+   the calculations in <xref target="expiration.model" />.
+</t>
+<t>
+   A cache &MUST-NOT; generate a stale response if it is prohibited by an
+   explicit in-protocol directive (e.g., by a "no-cache" cache
+   directive, a "must-revalidate" cache-response-directive, or an applicable
+   "s-maxage" or "proxy-revalidate" cache-response-directive; see <xref
+   target="cache-response-directive"/>).
+</t>
+<t>
+   A cache &MUST-NOT; generate a stale response unless it is disconnected
+   or doing so is explicitly permitted by the client or origin server
+   (e.g., by the max-stale request directive in <xref
+   target="cache-request-directive" />, by extension directives such as those
+   defined in <xref target="RFC5861"/>, or by configuration in accordance
+   with an out-of-band contract).
+</t>
+</section>
+</section>
+
+<section title="Validation" anchor="validation.model">
+<t>
+   When a cache has one or more stored responses for a requested URI, but
+   cannot serve any of them (e.g., because they are not fresh, or one cannot
+   be selected; see <xref target="caching.negotiated.responses"/>), it can use
+   the conditional request mechanism (<xref target="preconditions"/>) in the forwarded request to
+   give the next inbound server an opportunity to select a valid stored
+   response to use, updating the stored metadata in the process, or to replace
+   the stored response(s) with a new response. This process is known as
+   <x:dfn>validating</x:dfn> or <x:dfn>revalidating</x:dfn> the stored
+   response.
+</t>
+
+<section title="Sending a Validation Request" anchor="validation.sent"><iref item="validator" />
+<t>
+   When generating a conditional request for validation, a cache starts with
+   either a request it is attempting to satisfy, or &mdash; if it is initiating
+   the request independently &mdash; it synthesises a request using a stored
+   response by copying the method, target URI, and request header fields
+   identified by the Vary header field (<xref
+   target="caching.negotiated.responses"/>).
+</t>
+<t>
+   It then updates that request with one or more precondition header fields.
+   These contain validator metadata sourced from stored response(s) that have
+   the same cache key.
+</t>
+<t>
+   The precondition header fields are then compared by recipients to
+   determine whether any stored response is equivalent to a current
+   representation of the resource.
+</t>
+<t>
+   One such validator is the timestamp given in a <x:ref>Last-Modified</x:ref>
+   header field (<xref target="field.last-modified"/>), which can be used in an
+   <x:ref>If-Modified-Since</x:ref> header field for response validation, or
+   in an <x:ref>If-Unmodified-Since</x:ref> or <x:ref>If-Range</x:ref> header
+   field for representation selection (i.e., the client is referring
+   specifically to a previously obtained representation with that timestamp).
+</t>
+<t>
+   Another validator is the entity-tag given in an <x:ref>ETag</x:ref>
+   field (<xref target="field.etag"/>). One or more entity-tags, indicating one or more
+   stored responses, can be used in an <x:ref>If-None-Match</x:ref> header
+   field for response validation, or in an <x:ref>If-Match</x:ref> or
+   <x:ref>If-Range</x:ref> header field for representation selection (i.e.,
+   the client is referring specifically to one or more previously obtained
+   representations with the listed entity-tags).
+</t>
+</section>
+
+<section title="Handling a Received Validation Request" anchor="validation.received">
+<t>
+   Each client in the request chain may have its own cache, so it is common
+   for a cache at an intermediary to receive conditional requests from other
+   (outbound) caches. Likewise, some user agents make use of conditional
+   requests to limit data transfers to recently modified representations or to
+   complete the transfer of a partially retrieved representation.
+</t>
+<t>
+   If a cache receives a request that can be satisfied by reusing one of
+   its stored <x:ref>200 (OK)</x:ref> or <x:ref>206 (Partial Content)</x:ref>
+   responses, the cache &SHOULD; evaluate any applicable conditional header
+   field preconditions received in that request with respect to the
+   corresponding validators contained within the selected response.
+   A cache &MUST-NOT; evaluate conditional header fields that only
+   apply to an origin server, occur in a request with semantics that
+   cannot be satisfied with a cached response, or occur in a request with a target resource
+   for which it has no stored responses; such preconditions are likely
+   intended for some other (inbound) server.
+</t>
+<t>
+   The proper evaluation of conditional requests by a cache depends on the
+   received precondition header fields and their precedence. In summary, the <x:ref>If-Match</x:ref> and
+   <x:ref>If-Unmodified-Since</x:ref> conditional header fields are not
+   applicable to a cache, and <x:ref>If-None-Match</x:ref> takes precedence over
+   <x:ref>If-Modified-Since</x:ref>. See <xref target="precedence"/> for
+   a complete specification of precondition precedence.
+</t>
+<t>
+   A request containing an <x:ref>If-None-Match</x:ref> header field
+   (<xref target="field.if-none-match"/>) indicates that the client wants to validate one
+   or more of its own stored responses in comparison to whichever stored
+   response is selected by the cache.
+</t>
+<t>
+   When a cache decides to revalidate its own stored responses for a
+   request that contains an <x:ref>If-None-Match</x:ref> list of entity-tags,
+   the cache &MAY; combine the received list with a list of entity-tags
+   from its own stored set of responses (fresh or stale) and send the union of
+   the two lists as a replacement <x:ref>If-None-Match</x:ref> header
+   field value in the forwarded request.
+   If a stored response contains only partial content, the
+   cache &MUST-NOT; include its entity-tag in the union unless the request is
+   for a range that would be fully satisfied by that partial stored response.
+   If the response to the forwarded request is
+   <x:ref>304 (Not Modified)</x:ref> and has an ETag field value with
+   an entity-tag that is not in the client's list, the cache &MUST;
+   generate a <x:ref>200 (OK)</x:ref> response for the client by reusing its
+   corresponding stored response, as updated by the 304 response metadata
+   (<xref target="freshening.responses"/>).
+</t>
+<t>
+   If an <x:ref>If-None-Match</x:ref> header field is not present, a request
+   containing an <x:ref>If-Modified-Since</x:ref> header field
+   (<xref target="field.if-modified-since"/>) indicates that the client wants to validate
+   one or more of its own stored responses by modification date.
+</t>
+<t>
+   If a request contains an <x:ref>If-Modified-Since</x:ref> header field and
+   the <x:ref>Last-Modified</x:ref> header field is not present in a selected
+   stored response, a cache &SHOULD; use the stored response's
+   <x:ref>Date</x:ref> field value (or, if no Date field is present, the time
+   that the stored response was received) to evaluate the conditional.
+</t>
+<t>
+   A cache that implements partial responses to range requests, as defined in
+   <xref target="field.range"/>, also needs to evaluate a received <x:ref>If-Range</x:ref> header
+   field (<xref target="field.if-range"/>) regarding its selected stored response.
+</t>
+</section>
+
+<section title="Handling a Validation Response" anchor="validation.response">
+<t>
+   Cache handling of a response to a conditional request depends upon its
+   status code:
+</t>
+<ul>
+   <li>
+     A <x:ref>304 (Not Modified)</x:ref> response status code indicates
+     that the stored response can be updated and reused; see <xref
+     target="freshening.responses"/>.
+   </li>
+   <li>
+     A full response (i.e., one containing content) indicates that none
+     of the stored responses nominated in the conditional request is
+     suitable. Instead, the cache &MUST; use the full response to
+     satisfy the request. The cache &MAY; store such a full response,
+     subject to its constraints (see <xref target="response.cacheability"/>).
+   </li>
+   <li>
+     However, if a cache receives a <x:ref>5xx (Server Error)</x:ref>
+     response while attempting to validate a response, it can either
+     forward this response to the requesting client, or act as if the
+     server failed to respond. In the latter case, the cache can send a
+     previously stored response, subject to its constraints on doing so (see <xref
+     target="serving.stale.responses" />), or retry the validation request.
+   </li>
+</ul>
+</section>
+
+<section title="Freshening Stored Responses upon Validation" anchor="freshening.responses">
+<t>
+   When a cache receives a <x:ref>304 (Not Modified)</x:ref> response and
+   already has one or more stored <x:ref>200 (OK)</x:ref> responses for the
+   applicable cache key, the cache needs to identify which (if any) are to
+   be updated by the new information provided, and then do so.
+</t>
+<iref item="strong validator" />
+<t>
+   The stored response(s) to update are identified by using the first match
+   (if any) of:
+</t>
+<ul>
+   <li>
+     If the new response contains one or more <x:dfn>strong validators</x:dfn> (see
+     <xref target="weak.and.strong.validators"/>), then each of those strong validators
+     identify the selected representation for update. All the stored
+     responses with one of those same strong validators are identified for update. If
+     none of the stored responses contain at least one of the same strong validators, then the
+     cache &MUST-NOT; use the new response to update any stored responses.
+   </li>
+   <li>
+     If the new response contains no strong validators but does contain
+     one or more <x:dfn>weak validators</x:dfn>, and those
+     validators correspond to one of the cache's stored responses, then the most
+     recent of those matching stored responses is identified for update.
+   </li>
+   <li>
+     If the new response does not include any form of validator (such as
+     where a client generates an <x:ref>If-Modified-Since</x:ref> request from
+     a source other than the <x:ref>Last-Modified</x:ref> response header
+     field), and there is only one stored response, and that stored response
+     also lacks a validator, then that stored response is identified for update.
+   </li>
+</ul>
+<t>
+   For each stored response identified, the cache &MUST; update
+   its header fields with the header fields provided in the <x:ref>304 (Not
+   Modified)</x:ref> response, as per <xref target="update"/>.
+</t>
+</section>
+
+<section title="Freshening Responses with HEAD" anchor="head.effects">
+<t>
+   A response to the HEAD method is identical to what an equivalent request
+   made with a GET would have been, without sending the content. This property
+   of HEAD responses can be used to invalidate or update a cached GET
+   response if the more efficient conditional GET request mechanism is not
+   available (due to no validators being present in the stored response) or
+   if transmission of the content is not desired even if it has
+   changed.
+</t>
+<t>
+   When a cache makes an inbound HEAD request for a target URI and
+   receives a <x:ref>200 (OK)</x:ref> response, the cache &SHOULD; update or
+   invalidate each of its stored GET responses that could have been selected
+   for that request (see <xref target="caching.negotiated.responses"/>).
+</t>
+<t>
+   For each of the stored responses that could have been selected, if the
+   stored response and HEAD response have matching values for any received
+   validator fields (<x:ref>ETag</x:ref> and <x:ref>Last-Modified</x:ref>)
+   and, if the HEAD response has a <x:ref>Content-Length</x:ref> header field,
+   the value of <x:ref>Content-Length</x:ref> matches that of the stored
+   response, the cache &SHOULD; update the stored response as described below;
+   otherwise, the cache &SHOULD; consider the stored response to be stale.
+</t>
+<t>
+   If a cache updates a stored response with the metadata provided in a HEAD
+   response, the cache &MUST; use the header fields provided in the HEAD
+   response to update the stored response (see <xref
+   target="update"/>).
+</t>
+</section>
+</section>
+
+<section title="Invalidating Stored Responses" anchor="invalidation">
+<t>
+   Because unsafe request methods (<xref target="safe.methods"/>) such as PUT, POST or DELETE
+   have the potential for changing state on the origin server, intervening
+   caches are required to invalidate stored responses to keep their contents up to date.
+</t>
+<t>
+   A cache &MUST; invalidate the target URI
+   (<xref target="target.resource"/>) when a non-error status code is received in response to
+   an unsafe request method (including methods whose safety is unknown).
+</t>
+<t>
+   A cache &MAY; invalidate other URIs when a non-error status code is received
+   in response to an unsafe request method (including methods whose safety is unknown).
+   In particular, the URI(s) in the
+   <x:ref>Location</x:ref> and <x:ref>Content-Location</x:ref> response header
+   fields (if present) are candidates for invalidation; other URIs might be discovered
+   through mechanisms not specified in this document.
+   However, a cache &MUST-NOT; trigger an invalidation under these conditions
+   if the origin (<xref target="origin"/>) of the URI to be invalidated differs from that of the target URI
+   (<xref target="target.resource"/>). This helps prevent denial-of-service attacks.
+</t>
+<t>
+   <x:dfn>Invalidate</x:dfn> means that the cache will either remove all
+   stored responses whose target URI matches the given URI, or will mark them
+   as "invalid" and in need of a mandatory validation before they can be sent
+   in response to a subsequent request.
+</t>
+<t>
+   A "non-error response" is one with a <x:ref>2xx (Successful)</x:ref>
+   or <x:ref>3xx (Redirection)</x:ref> status code.
+</t>
+<t>
+   Note that this does not guarantee that all appropriate responses are
+   invalidated globally; a state-changing request would only invalidate
+   responses in the caches it travels through.
+</t>
+</section>
+</section>
+
+
+
+<section title="Field Definitions" anchor="header.field.definitions">
+<t>
+   This section defines the syntax and semantics of HTTP fields
+   related to caching.
+</t>
+
+<section title="Age" anchor="field.age">
+  <x:anchor-alias value="header.age"/>
+   <iref primary="true" item="Fields" subitem="Age" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Age" x:for-anchor=""/><iref primary="true" item="Fields" subitem="Age" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Age" x:for-anchor=""/><iref item="Age header field" primary="true" x:for-anchor=""/>
+   <x:anchor-alias value="Age"/>
+   <x:anchor-alias value="age-value"/>
+<t>
+   The "Age" response header field conveys the sender's estimate of the
+   time since the response was generated or successfully validated at the
+   origin server. Age values are calculated as specified in <xref
+   target="age.calculations" />.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Age"/>
+  <x:ref>Age</x:ref> = <x:ref>delta-seconds</x:ref>
+</sourcecode>
+<t>
+   The Age field value is a non-negative integer, representing time in seconds
+   (see <xref target="delta-seconds"/>).
+</t>
+<t>
+   Although it is defined as a singleton header field, a cache encountering a
+   message with multiple Age field lines &SHOULD; use the
+   first field line, discarding subsequent ones.
+</t>
+<t>
+   If the field value (after discarding additional lines, as per above) is invalid
+   (e.g., it contains a list or something other than a non-negative integer),
+   a cache &SHOULD; consider the response to be stale.
+</t>
+<t>
+   The presence of an Age header field implies that the response was not
+   generated or validated by the origin server for this request. However,
+   lack of an Age header field does not imply the origin was contacted.
+</t>
+</section>
+
+<section title="Cache-Control" anchor="field.cache-control">
+  <x:anchor-alias value="header.cache-control"/>
+   <iref primary="true" item="Fields" subitem="Cache-Control" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Cache-Control" x:for-anchor=""/><iref item="Cache-Control header field" primary="true" x:for-anchor=""/>
+   <x:anchor-alias value="Cache-Control"/>
+   <x:anchor-alias value="cache-directive"/>
+<t>
+   The "Cache-Control" header field is used to list directives for caches
+   along the request/response chain. Such cache directives are unidirectional
+   in that the presence of a directive in a request does not imply that the
+   same directive is present in the response, or to be repeated in it.
+</t>
+<t>
+   See <xref target="cache.control.extensions"/> for information about how
+   Cache-Control directives defined elsewhere are handled.
+</t>
+<t>
+   A proxy, whether or not it implements a cache, &MUST; pass cache directives
+   through in forwarded messages, regardless of their
+   significance to that application, since the directives might apply
+   to all recipients along the request/response chain. It is not possible to
+   target a directive to a specific cache.
+</t>
+<t>
+   Cache directives are identified by a token, to be compared case-insensitively,
+   and have an optional argument that can use both token and quoted-string
+   syntax. For the directives defined below that define arguments, recipients
+   ought to accept both forms, even if a specific form is required for generation.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Cache-Control"/><iref primary="true" item="Grammar" subitem="cache-directive"/>
+  <x:ref>Cache-Control</x:ref>   = #<x:ref>cache-directive</x:ref>
+
+  <x:ref>cache-directive</x:ref> = <x:ref>token</x:ref> [ "=" ( <x:ref>token</x:ref> / <x:ref>quoted-string</x:ref> ) ]
+</sourcecode>
+<t>
+   For the cache directives defined below, no argument is defined (nor allowed)
+   unless stated otherwise.
+</t>
+
+<section title="Request Cache-Control Directives" anchor="cache-request-directive" x:assert-sorted-by="title">
+
+<t>
+   This section defines cache request directives. They are advisory; caches
+   &MAY; implement them, but are not required to.
+</t>
+
+
+<section title="max-age" anchor="cache-request-directive.max-age">
+   <iref item="max-age (cache directive)" primary="true"/>
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li><x:ref>delta-seconds</x:ref> (see <xref target="delta-seconds"/>)</li>
+</ul>
+<t>
+   The "max-age" request directive indicates that the client prefers a
+   response whose age is less than or equal to the specified number of
+   seconds. Unless the max-stale request directive is also present, the
+   client does not wish to receive a stale response.
+</t>
+<t>
+   This directive uses the token form of the argument syntax:
+   e.g., 'max-age=5' not 'max-age="5"'. A sender &MUST-NOT; generate the
+   quoted-string form.
+</t>
+</section>
+
+<section title="max-stale" anchor="cache-request-directive.max-stale">
+   <iref item="max-stale (cache directive)" primary="true"/>
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li><x:ref>delta-seconds</x:ref> (see <xref target="delta-seconds"/>)</li>
+</ul>
+<t>
+   The "max-stale" request directive indicates that the client will
+   accept a response that has exceeded its freshness lifetime. If a value is
+   present, then the client is willing to accept a response that has exceeded
+   its freshness lifetime by no more than the specified number of seconds. If
+   no value is assigned to max-stale, then the client will accept a
+   stale response of any age.
+</t>
+<t>
+   This directive uses the token form of the argument syntax:
+   e.g., 'max-stale=10' not 'max-stale="10"'. A sender &MUST-NOT; generate
+   the quoted-string form.
+</t>
+</section>
+
+<section title="min-fresh" anchor="cache-request-directive.min-fresh">
+   <iref item="min-fresh (cache directive)" primary="true"/>
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li><x:ref>delta-seconds</x:ref> (see <xref target="delta-seconds"/>)</li>
+</ul>
+<t>
+   The "min-fresh" request directive indicates that the client prefers a
+   response whose freshness lifetime is no less than its current age plus the
+   specified time in seconds. That is, the client wants a response that will
+   still be fresh for at least the specified number of seconds.
+</t>
+<t>
+   This directive uses the token form of the argument syntax:
+   e.g., 'min-fresh=20' not 'min-fresh="20"'. A sender &MUST-NOT; generate
+   the quoted-string form.
+</t>
+</section>
+
+<section title="no-cache" anchor="cache-request-directive.no-cache">
+   <iref item="no-cache (cache directive)" primary="true" />
+<t>
+   The "no-cache" request directive indicates that the client prefers
+   stored response not be used to satisfy the request without successful
+   validation on the origin server.
+</t>
+</section>
+
+<section title="no-store" anchor="cache-request-directive.no-store">
+   <iref item="no-store (cache directive)" primary="true" />
+<t>
+   The "no-store" request directive indicates that a cache &MUST-NOT;
+   store any part of either this request or any response to it. This
+   directive applies to both private and shared caches. "MUST NOT
+   store" in this context means that the cache &MUST-NOT; intentionally
+   store the information in non-volatile storage, and &MUST; make a
+   best-effort attempt to remove the information from volatile storage as
+   promptly as possible after forwarding it.
+</t>
+<t>
+   This directive is <em>not</em> a reliable or sufficient mechanism for ensuring
+   privacy. In particular, malicious or compromised caches might not
+   recognize or obey this directive, and communications networks might be
+   vulnerable to eavesdropping.
+</t>
+<t>
+   Note that if a request containing this directive is satisfied from a
+   cache, the no-store request directive does not apply to the already
+   stored response.
+</t>
+</section>
+
+<section title="no-transform" anchor="cache-request-directive.no-transform">
+   <iref item="no-transform (cache directive)" primary="true" />
+<t>
+   The "no-transform" request directive indicates that the client is asking
+   for intermediaries to avoid
+   transforming the content, as defined in <xref
+   target="message.transformations"/>.
+</t>
+</section>
+
+<section title="only-if-cached" anchor="cache-request-directive.only-if-cached">
+   <iref item="only-if-cached (cache directive)" primary="true" />
+<t>
+   The "only-if-cached" request directive indicates that the client only
+   wishes to obtain a stored response. Caches that honor this request
+   directive &SHOULD;, upon receiving it, either respond using a stored
+   response consistent with the other constraints of the request, or
+   respond with a <x:ref>504 (Gateway Timeout)</x:ref> status code.
+</t>
+</section>
+</section>
+
+<section title="Response Cache-Control Directives" anchor="cache-response-directive" x:assert-sorted-by="title">
+   <x:anchor-alias value="cache-response-directive" />
+
+<t>
+   This section defines cache response directives. A cache &MUST; obey the
+   Cache-Control directives defined in this section.
+</t>
+
+<section title="max-age" anchor="cache-response-directive.max-age">
+   <iref item="max-age (cache directive)" primary="true" />
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li><x:ref>delta-seconds</x:ref> (see <xref target="delta-seconds"/>)</li>
+</ul>
+<t>
+   The "max-age" response directive indicates that the response is to be
+   considered stale after its age is greater than the specified number of
+   seconds.
+</t>
+<t>
+   This directive uses the token form of the argument syntax:
+   e.g., 'max-age=5' not 'max-age="5"'. A sender &MUST-NOT; generate the
+   quoted-string form.
+</t>
+</section>
+
+<section title="must-revalidate" anchor="cache-response-directive.must-revalidate">
+   <iref item="must-revalidate (cache directive)" primary="true" />
+<t>
+   The "must-revalidate" response directive indicates that once the response
+   has become stale, a cache &MUST-NOT; reuse that response to satisfy
+   another request until it has been successfully validated by the origin, as
+   defined by <xref target="validation.model"/>.
+</t>
+<t>
+   The must-revalidate directive is necessary to support reliable operation
+   for certain protocol features. In all circumstances a cache &MUST-NOT; ignore
+   the must-revalidate directive; in particular, if a cache is disconnected,
+   the cache &MUST; generate an error response rather than reuse the stale response.
+   The generated status code &SHOULD; be <x:ref>504 (Gateway Timeout)</x:ref>
+   unless another error status code is more applicable.
+</t>
+<t>
+   The must-revalidate directive ought to be used by servers if and only
+   if failure to validate a request could cause
+   incorrect operation, such as a silently unexecuted financial
+   transaction.
+</t>
+<t>
+   The must-revalidate directive also permits a shared cache to
+   reuse a response to a request containing an <x:ref>Authorization</x:ref>
+   header field (<xref target="field.authorization"/>),
+   subject to the above requirement on revalidation
+   (<xref target="caching.authenticated.responses"/>).
+</t>
+</section>
+
+<section title="must-understand" anchor="cache-response-directive.must-understand">
+   <iref item="must-understand (cache directive)" primary="true" />
+<t>
+   The "must-understand" response directive limits caching of the response to
+   a cache that understands and conforms to the requirements for that
+   response's status code.
+</t>
+<t>
+   Responses containing "must-understand" &SHOULD; also contain the "no-store" directive;
+   caches that implement "must-understand" &SHOULD; ignore the "no-store" directive
+   in responses that contain both directives and a status code that the cache
+   understands and conforms to any related caching requirements.
+</t>
+</section>
+
+<section title="no-cache" anchor="cache-response-directive.no-cache">
+   <iref item="no-cache (cache directive)" primary="true" />
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li>#<x:ref>field-name</x:ref></li>
+</ul>
+<t>
+   The "no-cache" response directive, in its unqualified form (without an
+   argument), indicates that the response &MUST-NOT; be used to satisfy any
+   other request without forwarding it for validation and receiving a
+   successful response; see <xref target="validation.model"/>.
+</t>
+<t>
+   This allows an origin server to prevent a cache from using
+   the response to satisfy a request without contacting it, even by caches that have
+   been configured to send stale responses.
+</t>
+<t>
+   The qualified form of no-cache response directive, with an argument that
+   lists one or more field names, indicates that a cache &MAY; use the
+   response to satisfy a subsequent request, subject to any other restrictions
+   on caching, if the listed header fields are excluded from the subsequent
+   response or the subsequent response has been successfully revalidated with
+   the origin server (updating or removing those fields).
+   This allows an origin server to prevent the re-use of certain header
+   fields in a response, while still allowing caching of the rest of the
+   response.
+</t>
+<t>
+   The field names given are not limited to the set of header
+   fields defined by this specification. Field names are case-insensitive.
+</t>
+<t>
+   This directive uses the quoted-string form of the argument syntax.
+   A sender &SHOULD-NOT; generate the token form (even if quoting appears not
+   to be needed for single-entry lists).
+</t>
+<aside><t>
+   &Note; The
+   qualified form of the directive is often handled by caches as if an
+   unqualified no-cache directive was received; i.e., the special handling
+   for the qualified form is not widely implemented.
+</t></aside>
+</section>
+
+<section title="no-store" anchor="cache-response-directive.no-store">
+   <iref item="no-store (cache directive)" primary="true" />
+<t>
+   The "no-store" response directive indicates that a cache &MUST-NOT; store
+   any part of either the immediate request or response, and &MUST-NOT; use
+   the response to satisfy any other request.
+</t>
+<t>
+   This directive applies to both private and shared caches. "MUST NOT
+   store" in this context means that the cache &MUST-NOT; intentionally store
+   the information in non-volatile storage, and &MUST; make a best-effort
+   attempt to remove the information from volatile storage as promptly as
+   possible after forwarding it.
+</t>
+<t>
+   This directive is <em>not</em> a reliable or sufficient mechanism for ensuring
+   privacy. In particular, malicious or compromised caches might not
+   recognize or obey this directive, and communications networks might be
+   vulnerable to eavesdropping.
+</t>
+<t>
+   Note that the "must-understand" cache directive overrides "no-store" in certain
+   circumstances; see <xref target="cache-response-directive.must-understand"/>.
+</t>
+</section>
+
+<section title="no-transform" anchor="cache-response-directive.no-transform">
+   <iref item="no-transform (cache directive)" primary="true" />
+<t>
+   The "no-transform" response directive indicates that an intermediary
+   (regardless of whether it implements a cache) &MUST-NOT; transform the
+   content, as defined in <xref target="message.transformations"/>.
+</t>
+</section>
+
+<section title="private" anchor="cache-response-directive.private">
+   <iref item="private (cache directive)" primary="true" />
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li>#<x:ref>field-name</x:ref></li>
+</ul>
+<t>
+   The unqualified "private" response directive indicates that
+   a shared cache &MUST-NOT; store the response (i.e., the response is
+   intended for a single user).
+   It also indicates that a private cache &MAY; store the response, subject
+   the constraints defined in <xref target="response.cacheability"/>, even if
+   the response would not otherwise be heuristically cacheable by a private
+   cache.
+</t>
+<t>
+   If a qualified private response directive is present, with an argument that
+   lists one or more field names, then only the listed header fields are limited to a
+   single user: a shared cache &MUST-NOT; store the listed header fields if they
+   are present in the original response, but &MAY; store the remainder of the
+   response message without those header fields, subject
+   the constraints defined in <xref target="response.cacheability"/>.
+</t>
+<t>
+   The field names given are not limited to the set of header
+   fields defined by this specification. Field names are case-insensitive.
+</t>
+<t>
+   This directive uses the quoted-string form of the argument syntax.
+   A sender &SHOULD-NOT; generate the token form (even if quoting appears not
+   to be needed for single-entry lists).
+</t>
+<aside><t>
+   &Note; This usage of the word "private" only controls
+   where the response can be stored; it cannot ensure the privacy of the
+   message content. Also, the qualified form of the directive is
+   often handled by caches as if an unqualified private directive
+   was received; i.e., the special handling for the qualified form is not
+   widely implemented.
+</t></aside>
+</section>
+
+<section title="proxy-revalidate" anchor="cache-response-directive.proxy-revalidate">
+   <iref item="proxy-revalidate (cache directive)" primary="true" />
+<t>
+   The "proxy-revalidate" response directive indicates that once the response
+   has become stale, a shared cache &MUST-NOT; reuse that response to satisfy
+   another request until it has been successfully validated by the origin,
+   as defined by <xref target="validation.model"/>. This is analogous to
+   must-revalidate (<xref target="cache-response-directive.must-revalidate"/>),
+   except that proxy-revalidate does not apply to private caches.
+</t>
+<t>
+   Note that "proxy-revalidate" on its own does not imply that a response is
+   cacheable. For example, it might be combined with the public directive
+   (<xref target="cache-response-directive.public"/>), allowing the response
+   to be cached while requiring only a shared cache to revalidate when stale.
+</t>
+</section>
+
+<section title="public" anchor="cache-response-directive.public">
+   <iref item="public (cache directive)" primary="true" />
+<t>
+   The "public" response directive indicates that a cache &MAY; store the
+   response even if it would otherwise be prohibited, subject to the
+   constraints defined in <xref target="response.cacheability"/>. In other words,
+   public explicitly marks the response as cacheable. For example,
+   public permits a shared cache to reuse a response to a request containing
+   an Authorization header field (<xref target="caching.authenticated.responses"/>).
+</t>
+<t>
+   Note that it is unnecessary to add the public directive to a response that
+   is already cacheable according to <xref target="response.cacheability"/>.
+</t>
+<t>
+   If a response with the public directive has no explicit freshness information,
+   it is heuristically cacheable (<xref target="heuristic.freshness"/>).
+</t>
+</section>
+
+<section title="s-maxage" anchor="cache-response-directive.s-maxage">
+   <iref item="s-maxage (cache directive)" primary="true" />
+<t>
+   Argument syntax:
+</t>
+<ul empty="true">
+  <li><x:ref>delta-seconds</x:ref> (see <xref target="delta-seconds"/>)</li>
+</ul>
+<t>
+   The "s-maxage" response directive indicates that, for a shared cache, the
+   maximum age specified by this directive overrides the maximum age
+   specified by either the max-age directive or the <x:ref>Expires</x:ref>
+   header field.
+</t>
+<t>
+   The s-maxage directive incorporates the
+   proxy-revalidate (<xref target="cache-response-directive.proxy-revalidate"/>)
+   response directive's semantics for a shared cache.
+   A shared cache &MUST-NOT; reuse a stale response with s-maxage to satisfy
+   another request until it has been successfully validated by the origin, as
+   defined by <xref target="validation.model"/>.
+   This directive also permits a shared cache to reuse a response to a
+   request containing an Authorization header field, subject to the above
+   requirements on maximum age and revalidation
+   (<xref target="caching.authenticated.responses"/>).
+</t>
+<t>
+   This directive uses the token form of the argument syntax:
+   e.g., 's-maxage=10' not 's-maxage="10"'. A sender &MUST-NOT; generate
+   the quoted-string form.
+</t>
+</section>
+</section>
+
+<section title="Cache Control Extensions" anchor="cache.control.extensions">
+<t>
+   The Cache-Control header field can be extended through the use of one or
+   more cache-extension tokens, each with an optional value.
+   A cache &MUST; ignore unrecognized cache directives.
+</t>
+<t>
+   Informational extensions (those that do not require a change in cache
+   behavior) can be added without changing the semantics of other directives.
+</t>
+<t>
+   Behavioral extensions are designed to work by acting as modifiers to the
+   existing base of cache directives.
+   Both the new directive and the old directive are supplied, such that
+   applications that do not understand the new directive will default to the
+   behavior specified by the old directive, and those that understand the
+   new directive will recognize it as modifying the requirements associated
+   with the old directive. In this way, extensions to the existing
+   cache-control directives can be made without breaking deployed caches.
+</t>
+<t>
+   For example, consider a hypothetical new response directive called
+   "community" that acts as a modifier to the private directive: in addition
+   to private caches, any cache that is shared only by members of the named
+   community is allowed to cache the response. An origin server wishing to
+   allow the UCI community to use an otherwise private response in their
+   shared cache(s) could do so by including
+</t>
+<artwork type="example">
+  Cache-Control: private, community="UCI"
+</artwork>
+<t>
+   A cache that recognizes such a community cache-extension could broaden its
+   behavior in accordance with that extension.  A cache that does not
+   recognize the community cache-extension would ignore it and adhere to the
+   private directive.
+</t>
+<t>
+   New extension directives ought to consider defining:
+</t>
+<ul>
+  <li>What it means for a directive to be specified multiple times,</li>
+  <li>When the directive does not take an argument, what it means when an
+  argument is present,</li>
+  <li>When the directive requires an argument, what it means when it is
+  missing,</li>
+  <li>Whether the directive is specific to requests, responses, or able
+  to be used in either.</li>
+</ul>
+</section>
+
+<section title="Cache Directive Registry" anchor="cache.directive.registry">
+<t>
+   The "Hypertext Transfer Protocol (HTTP) Cache Directive Registry" defines the namespace for the
+   cache directives. It has been created and is now maintained at
+   <eref target="https://www.iana.org/assignments/http-cache-directives"/>.
+</t>
+<t>
+   A registration &MUST; include the following fields:
+</t>
+<ul>
+  <li>Cache Directive Name</li>
+  <li>Pointer to specification text</li>
+</ul>
+<t>
+   Values to be added to this namespace require IETF Review (see <xref
+   target="RFC8126" x:fmt="," x:sec="4.8"/>).
+</t>
+</section>
+</section>
+
+<section title="Expires" anchor="field.expires">
+  <x:anchor-alias value="header.expires"/>
+   <iref primary="true" item="Fields" subitem="Expires" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Expires" x:for-anchor=""/><iref primary="true" item="Fields" subitem="Expires" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Expires" x:for-anchor=""/><iref item="Expires header field" primary="true" x:for-anchor=""/>
+   <x:anchor-alias value="Expires"/>
+<t>
+   The "Expires" response header field gives the date/time after which the
+   response is considered stale. See <xref target="expiration.model" /> for
+   further discussion of the freshness model.
+</t>
+<t>
+   The presence of an Expires header field does not imply that the original resource
+   will change or cease to exist at, before, or after that time.
+</t>
+<t>
+   The Expires field value is an HTTP-date timestamp, as defined in <xref target="http.date"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Expires"/>
+  <x:ref>Expires</x:ref> = <x:ref>HTTP-date</x:ref>
+</sourcecode>
+<t>
+   For example
+</t>
+<artwork type="example">
+  Expires: Thu, 01 Dec 1994 16:00:00 GMT
+</artwork>
+<t>
+   A cache recipient &MUST; interpret invalid date formats, especially the
+   value "0", as representing a time in the past (i.e., "already expired").
+</t>
+<t>
+   If a response includes a <x:ref>Cache-Control</x:ref> header field with
+   the max-age directive (<xref target="cache-response-directive.max-age"/>),
+   a recipient &MUST; ignore the Expires header field.
+   Likewise, if a response includes the s-maxage directive
+   (<xref target="cache-response-directive.s-maxage" />), a shared cache
+   recipient &MUST; ignore the Expires header field. In both these cases, the value
+   in Expires is only intended for recipients that have not yet implemented
+   the Cache-Control header field.
+</t>
+<t>
+   An origin server without a clock &MUST-NOT; generate an Expires header field
+   unless its value represents a fixed time in the past (always expired)
+   or its value has been associated with the resource by a system or user
+   with a reliable clock.
+</t>
+<t>
+   Historically, HTTP required the Expires field value to be no more than a
+   year in the future. While longer freshness lifetimes are no longer
+   prohibited, extremely large values have been demonstrated to cause
+   problems (e.g., clock overflows due to use of 32-bit integers for
+   time values), and many caches will evict a response far sooner than
+   that.
+</t>
+</section>
+
+<section title="Pragma" anchor="field.pragma">
+  <x:anchor-alias value="header.pragma"/>
+   <iref primary="true" item="Fields" subitem="Pragma" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Pragma" x:for-anchor=""/><iref primary="true" item="Fields" subitem="Pragma" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Pragma" x:for-anchor=""/><iref item="Pragma header field" primary="true" x:for-anchor=""/>
+   <x:anchor-alias value="extension-pragma"/>
+   <x:anchor-alias value="Pragma"/>
+   <x:anchor-alias value="pragma-directive"/>
+<t>
+   The "Pragma" request header field was defined for HTTP/1.0 caches, so that clients
+   could specify a "no-cache" request (as <x:ref>Cache-Control</x:ref> was
+   not defined until HTTP/1.1).
+</t>
+<t>
+   However, support for Cache-Control is now widespread. As a result, this
+   specification deprecates Pragma.
+</t>
+
+<aside>
+   <t>
+      &Note; Because the meaning of "Pragma: no-cache" in responses was never
+      specified, it does not provide a reliable replacement for
+      "Cache-Control: no-cache" in them.
+   </t>
+</aside>
+</section>
+
+<section title="Warning" anchor="field.warning">
+  <x:anchor-alias value="header.warning"/>
+   <rdf:Description>
+     <status xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">obsoleted</status>
+   </rdf:Description>
+   <iref primary="true" item="Fields" subitem="Warning" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Warning" x:for-anchor=""/><iref item="Warning header field" primary="true" x:for-anchor=""/>
+   <x:anchor-alias value="Warning"/>
+<t>
+   The "Warning" header field was used to carry additional information
+   about the status or transformation of a message that might not be reflected
+   in the status code. This specification obsoletes it, as it is not widely
+   generated or surfaced to users. The information it carried can be gleaned
+   from examining other header fields, such as <x:ref>Age</x:ref>.
+</t>
+</section>
+</section>
+
+<section title="Relationship to Applications and Other Caches" anchor="history.lists">
+<t>
+   Applications using HTTP often specify additional forms of caching. For
+   example, Web browsers often have history mechanisms such as "Back" buttons
+   that can be used to redisplay a representation retrieved earlier in a
+   session.
+</t>
+<t>
+   Likewise, some Web browsers implement caching of images and other assets
+   within a page view; they may or may not honor HTTP caching semantics.
+</t>
+<t>
+   The requirements in this specification do not necessarily apply to how
+   applications use data after it is retrieved from a HTTP cache. For example, a
+   history mechanism can display a previous representation even if it has
+   expired, and an application can use cached data in other ways beyond its
+   freshness lifetime.
+</t>
+<t>
+   This specification does not prohibit the application from taking HTTP caching into
+   account; for example, a history mechanism might tell the user that a view
+   is stale, or it might honor cache directives (e.g., Cache-Control:
+   no-store).
+</t>
+<t>
+   However, when an application caches data and does not make this
+   apparent to or easily controllable by the user, it is strongly encouraged to
+   define its operation with respect to HTTP cache directives, so as
+   not to surprise authors who expect caching semantics
+   to be honoured. For example, while it might be reasonable to define an
+   application cache "above" HTTP that allows a response containing
+   Cache-Control: no-store to be reused for requests that are directly related
+   to the request that fetched it (such as those created during the same page
+   load), it would likely be surprising and confusing to users and authors if it
+   were allowed to be reused for requests unrelated in any way to the one from
+   which it was obtained.
+</t>
+</section>
+
+
+<section title="Security Considerations" anchor="security.considerations">
+<t>
+   This section is meant to inform developers, information providers, and
+   users of known security concerns specific to HTTP caching.
+   More general security considerations are addressed in "HTTP/1.1"
+   (<xref target="Messaging" x:rel="#security.considerations"/>)
+   and "HTTP Semantics"
+   (<xref target="Semantics" x:rel="#security.considerations"/>).
+</t>
+<t>
+   Caches expose additional potential vulnerabilities, since the contents of
+   the cache represent an attractive target for malicious exploitation.
+   Because cache contents persist after an HTTP request is complete, an attack
+   on the cache can reveal information long after a user believes that the
+   information has been removed from the network. Therefore, cache contents
+   need to be protected as sensitive information.
+</t>
+
+<section title="Cache Poisoning" anchor="cache.poisoning">
+<t>
+   Various attacks might be amplified by being stored in a cache. Such
+   "cache poisoning" attacks happen when an attacker uses
+   implementation flaws, elevated privileges, or other techniques to insert
+   a response into a cache. This is especially effective when shared caches
+   are used to distribute malicious content to many clients.
+</t>
+<t>
+   One common attack vector for cache poisoning is to exploit differences in
+   message parsing on proxies and in user agents; see <xref
+   target="message.body.length"/> for the relevant requirements regarding
+   HTTP/1.1.
+</t>
+</section>
+
+<section title="Timing Attacks" anchor="security.timing">
+<t>
+   Because one of the primary uses of a cache is to optimise performance,
+   its use can "leak" information about what resources have been previously
+   requested.
+</t>
+<t>
+   For example, if a user visits a site and their browser caches some of its
+   responses, and then navigates to a second site, that site can attempt to
+   load responses it knows exists on the first site. If they load
+   quickly, it can be assumed that the user has visited that site, or even
+   a specific page on it.
+</t>
+<t>
+   Such "timing attacks" can be mitigated by adding more information to the
+   cache key, such as the identity of the referring site (to prevent the
+   attack described above). This is sometimes called "double keying."
+</t>
+</section>
+
+<section title="Caching of Sensitive Information" anchor="caching.of.sensitive.information">
+<t>
+   Implementation and deployment flaws (as well as misunderstanding of cache
+   operation) might lead to caching of sensitive information (e.g.,
+   authentication credentials) that is thought to be private, exposing it to
+   unauthorized parties.
+</t>
+<t>
+   Note that the Set-Cookie response header field <xref target="RFC6265"/>
+   does not inhibit caching; a cacheable response with a Set-Cookie header
+   field can be (and often is) used to satisfy subsequent requests to caches.
+   Servers who wish to control caching of these responses are encouraged to
+   emit appropriate Cache-Control response header fields.
+</t>
+</section>
+</section>
+
+
+<section title="IANA Considerations" anchor="iana.considerations">
+<t>
+   The change controller for the following registrations is:
+   "IETF (iesg@ietf.org) - Internet Engineering Task Force".
+</t>
+
+<section title="Field Name Registration" anchor="field.name.registration">
+<t>
+   First, introduce the new "Hypertext Transfer Protocol (HTTP) Field
+   Name Registry" at <eref target="https://www.iana.org/assignments/http-fields"/>
+   as described in
+   <xref target="Semantics" x:rel="#field.name.registration"/>.
+</t>
+<t>
+   Then, please update the registry with the field names listed in the table
+   below:
+</t>
+<?BEGININC build/draft-ietf-httpbis-cache-latest.iana-headers ?>
+<!--AUTOGENERATED FROM extract-header-defs.xslt, do not edit manually-->
+<table align="left" anchor="iana.header.registration.table">
+   <thead>
+      <tr>
+         <th>Field Name</th>
+         <th>Status</th>
+         <th>Ref.</th>
+         <th>Comments</th>
+      </tr>
+   </thead>
+   <tbody>
+      <tr>
+         <td>Age</td>
+         <td>standard</td>
+         <td>
+            <xref target="field.age" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Cache-Control</td>
+         <td>standard</td>
+         <td>
+            <xref target="field.cache-control" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Expires</td>
+         <td>standard</td>
+         <td>
+            <xref target="field.expires" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Pragma</td>
+         <td>standard</td>
+         <td>
+            <xref target="field.pragma" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Warning</td>
+         <td>obsoleted</td>
+         <td>
+            <xref target="field.warning" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+   </tbody>
+</table>
+<!--(END)-->
+<?ENDINC build/draft-ietf-httpbis-cache-latest.iana-headers ?>
+</section>
+
+<section title="Cache Directive Registration" anchor="cache.directive.registration">
+<t>
+   Please update the
+   "Hypertext Transfer Protocol (HTTP) Cache Directive Registry"
+   at <eref target="https://www.iana.org/assignments/http-cache-directives"/>
+   with the registration procedure of <xref target="cache.directive.registry"/>
+   and the cache directive names summarized in the table below.
+</t>
+<?BEGININC build/draft-ietf-httpbis-cache-latest.cache-directives ?>
+<!--AUTOGENERATED FROM extract-cache-directives-defs.xslt, do not edit manually-->
+<table align="left" anchor="iana.cache.directive.registration.table">
+   <thead>
+      <tr>
+         <th>Cache Directive</th>
+         <th>Reference</th>
+      </tr>
+   </thead>
+   <tbody>
+      <tr>
+         <td>max-age</td>
+         <td>
+            <xref target="cache-request-directive.max-age"/>, <xref target="cache-response-directive.max-age"/>
+         </td>
+      </tr>
+      <tr>
+         <td>max-stale</td>
+         <td>
+            <xref target="cache-request-directive.max-stale"/>
+         </td>
+      </tr>
+      <tr>
+         <td>min-fresh</td>
+         <td>
+            <xref target="cache-request-directive.min-fresh"/>
+         </td>
+      </tr>
+      <tr>
+         <td>must-revalidate</td>
+         <td>
+            <xref target="cache-response-directive.must-revalidate"/>
+         </td>
+      </tr>
+      <tr>
+         <td>must-understand</td>
+         <td>
+            <xref target="cache-response-directive.must-understand"/>
+         </td>
+      </tr>
+      <tr>
+         <td>no-cache</td>
+         <td>
+            <xref target="cache-request-directive.no-cache"/>, <xref target="cache-response-directive.no-cache"/>
+         </td>
+      </tr>
+      <tr>
+         <td>no-store</td>
+         <td>
+            <xref target="cache-request-directive.no-store"/>, <xref target="cache-response-directive.no-store"/>
+         </td>
+      </tr>
+      <tr>
+         <td>no-transform</td>
+         <td>
+            <xref target="cache-request-directive.no-transform"/>, <xref target="cache-response-directive.no-transform"/>
+         </td>
+      </tr>
+      <tr>
+         <td>only-if-cached</td>
+         <td>
+            <xref target="cache-request-directive.only-if-cached"/>
+         </td>
+      </tr>
+      <tr>
+         <td>private</td>
+         <td>
+            <xref target="cache-response-directive.private"/>
+         </td>
+      </tr>
+      <tr>
+         <td>proxy-revalidate</td>
+         <td>
+            <xref target="cache-response-directive.proxy-revalidate"/>
+         </td>
+      </tr>
+      <tr>
+         <td>public</td>
+         <td>
+            <xref target="cache-response-directive.public"/>
+         </td>
+      </tr>
+      <tr>
+         <td>s-maxage</td>
+         <td>
+            <xref target="cache-response-directive.s-maxage"/>
+         </td>
+      </tr>
+   </tbody>
+</table>
+<!--(END)-->
+<?ENDINC build/draft-ietf-httpbis-cache-latest.cache-directives ?>
+</section>
+
+<section title="Warn Code Registry" anchor="warn.code.registration">
+<t>
+   Please add a note to the "Hypertext Transfer Protocol (HTTP) Warn Codes"
+   registry at <eref
+   target="https://www.iana.org/assignments/http-warn-codes"/> to the effect
+   that Warning is obsoleted.
+</t>
+</section>
+</section>
+</middle>
+
+<back>
+<references title="Normative References">
+
+  <reference anchor="Messaging">
+    <x:source basename="draft-ietf-httpbis-messaging-latest" href="rfc9110.xml">
+      <x:has anchor="message.body.length"/>
+    </x:source>
+  </reference>
+
+  <reference anchor="Semantics">
+    <x:source basename="draft-ietf-httpbis-semantics-latest" href="rfc9112.xml">
+      <x:defines>Content-Length</x:defines>
+      <x:has anchor="GET"/>
+      <x:has anchor="abnf.extension"/>
+      <x:has anchor="combining.byte.ranges"/>
+      <x:has anchor="field.authorization"/>
+      <x:has anchor="field.connection"/>
+      <x:has anchor="field.date"/>
+      <x:has anchor="field.etag"/>
+      <x:has anchor="field.if-modified-since"/>
+      <x:has anchor="field.if-none-match"/>
+      <x:has anchor="field.if-range"/>
+      <x:has anchor="field.last-modified"/>
+      <x:has anchor="field.lines"/>
+      <x:has anchor="field.proxy-authenticate"/>
+      <x:has anchor="field.proxy-authentication-info"/>
+      <x:has anchor="field.proxy-authorization"/>
+      <x:has anchor="field.range"/>
+      <x:has anchor="field.vary"/>
+      <x:has anchor="http.date"/>
+      <x:has anchor="field-names"/>
+      <x:has anchor="message.transformations"/>
+      <x:has anchor="messages"/>
+      <x:has anchor="origin"/>
+      <x:has anchor="overview.of.status.codes"/>
+      <x:has anchor="precedence"/>
+      <x:has anchor="preconditions"/>
+      <x:has anchor="quoted.strings"/>
+      <x:has anchor="safe.methods"/>
+      <x:has anchor="status.codes"/>
+      <x:has anchor="target.resource"/>
+      <x:has anchor="tokens"/>
+      <x:has anchor="weak.and.strong.validators"/>
+      <x:has anchor="whitespace"/>
+      <x:has anchor="Semantics-acks" target="acks"/>
+      <x:has anchor="Semantics-conformance" target="conformance"/>
+    </x:source>
+  </reference>
+
+  <reference anchor="RFC2119">
+    <front>
+      <title>Key words for use in RFCs to Indicate Requirement Levels</title>
+      <author initials="S." surname="Bradner" fullname="Scott Bradner"/>
+      <date month="March" year="1997"/>
+    </front>
+    <seriesInfo name="BCP" value="14"/>
+    <seriesInfo name="RFC" value="2119"/>
+  </reference>
+
+  <reference anchor="RFC8174">
+    <front>
+      <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
+      <author initials='B.' surname='Leiba' fullname='Barry Leiba'/>
+      <date year="2017" month="May"/>
+    </front>
+    <seriesInfo name='BCP' value='14'/>
+    <seriesInfo name='RFC' value='8174'/>
+  </reference>
+
+  <reference anchor="RFC5234">
+    <front>
+      <title abbrev="ABNF for Syntax Specifications">Augmented BNF for Syntax Specifications: ABNF</title>
+      <author initials="D." surname="Crocker" fullname="Dave Crocker" role="editor"/>
+      <author initials="P." surname="Overell" fullname="Paul Overell"/>
+      <date month="January" year="2008"/>
+    </front>
+    <seriesInfo name="STD" value="68"/>
+    <seriesInfo name="RFC" value="5234"/>
+  </reference>
+
+  <reference anchor="RFC7405">
+    <front>
+      <title>Case-Sensitive String Support in ABNF</title>
+      <author initials="P." surname="Kyzivat" fullname="Dave Kyzivat"/>
+      <date month="December" year="2014"/>
+    </front>
+    <seriesInfo name="RFC" value="7405"/>
+  </reference>
+
+</references>
+
+<references title="Informative References">
+
+  <reference anchor="RFC2616">
+    <front>
+      <title>Hypertext Transfer Protocol -- HTTP/1.1</title>
+      <author fullname="R. Fielding" initials="R." surname="Fielding"/>
+      <author fullname="J. Gettys" initials="J." surname="Gettys"/>
+      <author fullname="J. Mogul" initials="J." surname="Mogul"/>
+      <author fullname="H. Frystyk" initials="H." surname="Frystyk"/>
+      <author fullname="L. Masinter" initials="L." surname="Masinter"/>
+      <author fullname="P. Leach" initials="P." surname="Leach"/>
+      <author fullname="T. Berners-Lee" initials="T." surname="Berners-Lee"/>
+      <date month="June" year="1999" />
+    </front>
+    <seriesInfo name="RFC" value="2616" />
+  </reference>
+
+  <reference anchor="RFC7234">
+    <front>
+      <title>Hypertext Transfer Protocol (HTTP): Caching</title>
+      <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+      <author initials="M." surname="Nottingham" fullname="Mark Nottingham" role="editor"/>
+      <author initials="J. F." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+      <date month="June" year="2014"/>
+    </front>
+    <seriesInfo name="RFC" value="7234"/>
+  </reference>
+
+  <reference anchor='RFC5861'>
+    <front>
+      <title abbrev="HTTP stale controls">HTTP Cache-Control Extensions for Stale Content</title>
+      <author initials="M." surname="Nottingham" fullname="Mark Nottingham"/>
+      <date month="April" year="2010"/>
+    </front>
+    <seriesInfo name='RFC' value='5861' />
+  </reference>
+
+  <reference anchor='RFC5905'>
+    <front>
+      <title>Network Time Protocol Version 4: Protocol and Algorithms Specification</title>
+      <author initials='D.' surname='Mills' fullname='David L. Mills'/>
+      <author initials='J.' surname='Martin' fullname='Jim Martin' role="editor"/>
+      <author initials='J.' surname='Burbank' fullname='Jack Burbank'/>
+      <author initials='W.' surname='Kasch' fullname='William Kasch'/>
+      <date year='2010' month='June' />
+    </front>
+    <seriesInfo name='RFC' value='5905' />
+  </reference>
+
+  <reference anchor="RFC6265">
+    <front>
+      <title>HTTP State Management Mechanism</title>
+      <author initials="A." surname="Barth" fullname="Adam Barth"/>
+      <date year="2011" month="April" />
+    </front>
+    <seriesInfo name="RFC" value="6265"/>
+  </reference>
+
+  <reference anchor="RFC8126">
+    <front>
+      <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
+      <author initials="M." surname="Cotton" fullname="M. Cotton"/>
+      <author initials="B." surname="Leiba" fullname="B. Leiba"/>
+      <author initials="T." surname="Narten" fullname="T. Narten"/>
+      <date year="2017" month="June" />
+    </front>
+    <seriesInfo name="BCP" value="26"/>
+    <seriesInfo name="RFC" value="8126"/>
+  </reference>
+
+</references>
+
+<?BEGININC build/draft-ietf-httpbis-cache-latest.abnf-appendix ?>
+<section xmlns:x="http://purl.org/net/xml2rfc/ext" title="Collected ABNF" anchor="collected.abnf"><t>In the collected ABNF below, list rules are expanded as per <xref target="Semantics" x:rel="#abnf.extension.sender"/>.</t><sourcecode type="abnf" name="draft-ietf-httpbis-cache-latest.parsed-abnf">
+<x:ref>Age</x:ref> = delta-seconds
+
+<x:ref>Cache-Control</x:ref> = [ cache-directive *( OWS "," OWS cache-directive ) ]
+
+<x:ref>Expires</x:ref> = HTTP-date
+
+<x:ref>HTTP-date</x:ref> = &lt;HTTP-date, see <xref target="Semantics" x:fmt="," x:sec="5.6.7"/>&gt;
+
+<x:ref>OWS</x:ref> = &lt;OWS, see <xref target="Semantics" x:fmt="," x:sec="5.6.3"/>&gt;
+
+<x:ref>cache-directive</x:ref> = token [ "=" ( token / quoted-string ) ]
+
+<x:ref>delta-seconds</x:ref> = 1*DIGIT
+
+<x:ref>field-name</x:ref> = &lt;field-name, see <xref target="Semantics" x:fmt="," x:sec="5.1"/>&gt;
+
+<x:ref>quoted-string</x:ref> = &lt;quoted-string, see <xref target="Semantics" x:fmt="," x:sec="5.6.4"/>&gt;
+
+<x:ref>token</x:ref> = &lt;token, see <xref target="Semantics" x:fmt="," x:sec="5.6.2"/>&gt;
+</sourcecode>
+</section>
+<?ENDINC build/draft-ietf-httpbis-cache-latest.abnf-appendix ?>
+
+<section title="Changes from RFC 7234" anchor="changes.from.rfc.7234">
+<t>
+   Handling of duplicate and conflicting cache directives has been clarified.
+   (<xref target="calculating.freshness.lifetime"/>)
+</t>
+<t>
+   Cache invalidation of the URIs in the Location and Content-Location
+   header fields is no longer required, but still allowed.
+   (<xref target="invalidation"/>)
+</t>
+<t>
+   Cache invalidation of the URIs in the Location and Content-Location header fields is disallowed
+   when the origin is different; previously, it was the host.
+   (<xref target="invalidation"/>)
+</t>
+<t>
+   Handling invalid and multiple Age header field values has been clarified.
+   (<xref target="field.age"/>)
+</t>
+<t>
+   Some cache directives defined by this specification now have stronger
+   prohibitions against generating the quoted form of their values, since
+   this has been found to create interoperability problems. Consumers of
+   extension cache directives are no longer required to accept both token and
+   quoted-string forms, but they still need to parse them properly for
+   unknown extensions.
+   (<xref target="field.cache-control"/>)
+</t>
+<t>
+   The "public" and "private" cache directives were clarified, so that they
+   do not make responses reusable under any condition.
+   (<xref target="cache-response-directive"/>)
+</t>
+<t>
+   The "must-understand" cache directive was introduced; caches are no
+   longer required to understand the semantics of new response status codes
+   unless it is present.
+   (<xref target="cache-response-directive.must-understand"/>)
+</t>
+<t>
+   The Warning response header was obsoleted. Much of the information
+   supported by Warning could be gleaned by examining the response, and the
+   remaining warn-codes &mdash; although potentially useful &mdash; were entirely
+   advisory. In practice, Warning was not added by caches or intermediaries.
+   (<xref target="field.warning"/>)
+</t>
+</section>
+
+<section title="Change Log" anchor="change.log" removeInRFC="true">
+
+<section title="Between RFC7234 and draft 00" anchor="changes.since.publication.as.rfc">
+<t>
+  The changes were purely editorial:
+</t>
+<ul>
+  <li>Change boilerplate and abstract to indicate the "draft" status, and update references to ancestor specifications.</li>
+  <li>Remove version "1.1" from document title, indicating that this specification applies to all HTTP versions.</li>
+  <li>Adjust historical notes.</li>
+  <li>Update links to sibling specifications.</li>
+  <li>Replace sections listing changes from RFC 2616 by new empty sections referring to RFC 723x.</li>
+  <li>Remove acknowledgements specific to RFC 723x.</li>
+  <li>Move "Acknowledgements" to the very end and make them unnumbered.</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-00" anchor="changes.since.00">
+<t>
+  The changes are purely editorial:
+</t>
+<ul>
+  <li>Moved all extensibility tips, registration procedures, and registry
+      tables from the IANA considerations to normative sections, reducing the
+      IANA considerations to just instructions that will be removed prior to
+      publication as an RFC.</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-01" anchor="changes.since.01">
+<ul>
+  <li>Cite RFC 8126 instead of RFC 5226 (<eref target="https://github.com/httpwg/http-core/issues/75"/>)</li>
+  <li>In <xref target="field.pragma"/>, misleading statement about the relation between Pragma and Cache-Control (<eref target="https://github.com/httpwg/http-core/issues/92"/>, <eref target="https://www.rfc-editor.org/errata/eid4674"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-02" anchor="changes.since.02">
+<ul>
+   <li>In <xref target="response.cacheability"/>, explain that only final responses are cacheable (<eref target="https://github.com/httpwg/http-core/issues/29"/>)</li>
+   <li>In <xref target="cache-response-directive"/>, clarify what responses various directives apply to (<eref target="https://github.com/httpwg/http-core/issues/52"/>)</li>
+   <li>In <xref target="validation.sent"/>, clarify the source of validators in conditional requests (<eref target="https://github.com/httpwg/http-core/issues/110"/>)</li>
+   <li>Revise <xref target="history.lists"/> to apply to more than just History Lists (<eref target="https://github.com/httpwg/http-core/issues/126"/>)</li>
+   <li>In <xref target="field.warning"/>, deprecated "Warning" header field (<eref target="https://github.com/httpwg/http-core/issues/139"/>)</li>
+   <li>In <xref target="caching.authenticated.responses"/>, remove a spurious note (<eref target="https://github.com/httpwg/http-core/issues/141"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-03" anchor="changes.since.03">
+<ul>
+   <li>In <xref target="caching.overview"/>, define what a disconnected cache is (<eref target="https://github.com/httpwg/http-core/issues/5"/>)</li>
+   <li>In <xref target="constructing.responses.from.caches"/>, clarify language around how to select a response when more than one matches (<eref target="https://github.com/httpwg/http-core/issues/23"/>)</li>
+   <li>in <xref target="serving.stale.responses"/>, mention stale-while-revalidate and stale-if-error (<eref target="https://github.com/httpwg/http-core/issues/122"/>)</li>
+   <li>Remove requirements around cache request directives (<eref target="https://github.com/httpwg/http-core/issues/129"/>)</li>
+   <li>Deprecate Pragma (<eref target="https://github.com/httpwg/http-core/issues/140"/>)</li>
+   <li>In <xref target="caching.authenticated.responses"/> and <xref target="cache-response-directive"/>, note effect of some directives on authenticated requests (<eref target="https://github.com/httpwg/http-core/issues/161"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-04" anchor="changes.since.04">
+<ul>
+   <li>In <xref target="field.cache-control"/>, remove the registrations for stale-if-error and stale-while-revalidate which happened in RFC 7234 (<eref target="https://github.com/httpwg/http-core/issues/207"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-05" anchor="changes.since.05">
+<ul x:when-empty="None yet.">
+   <li>In <xref target="incomplete.responses"/>, clarify how weakly framed content is considered for purposes of completeness (<eref target="https://github.com/httpwg/http-core/issues/25"/>)</li>
+   <li>Throughout, describe Vary and cache key operations more clearly (<eref target="https://github.com/httpwg/http-core/issues/28"/>)</li>
+   <li>In <xref target="response.cacheability"/>, remove concept of "cacheable methods" in favor of prose (<eref target="https://github.com/httpwg/http-core/issues/54"/>, <eref target="https://www.rfc-editor.org/errata/eid5300"/>)</li>
+   <li>Refactored <xref target="security.considerations"/>, and added a section on timing attacks (<eref target="https://github.com/httpwg/http-core/issues/233"/>)</li>
+   <li>Changed "cacheable by default" to "heuristically cacheable" throughout (<eref target="https://github.com/httpwg/http-core/issues/242"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-06" anchor="changes.since.06">
+<ul x:when-empty="None yet.">
+   <li>In <xref target="response.cacheability"/> and <xref target="cache-response-directive.must-understand"/>, change response cacheability to only require understanding the response status code if the must-understand cache directive is present (<eref target="https://github.com/httpwg/http-core/issues/120"/>)</li>
+   <li>Change requirements for handling different forms of cache directives in <xref target="field.cache-control"/> (<eref target="https://github.com/httpwg/http-core/issues/128"/>)</li>
+   <li>Fix typo in <xref target="cache-response-directive.s-maxage"/> (<eref target="https://github.com/httpwg/http-core/issues/264"/>)</li>
+   <li>In <xref target="cache-response-directive.public"/> and <xref target="cache-response-directive.private"/>, clarify "private" and "public" so that they do not override all other cache directives (<eref target="https://github.com/httpwg/http-core/issues/268"/>)</li>
+   <li>In <xref target="response.cacheability"/>, distinguish between private with and without qualifying headers (<eref target="https://github.com/httpwg/http-core/issues/270"/>)</li>
+   <li>In <xref target="caching.negotiated.responses"/>, clarify that any "*" as a member of Vary will disable caching (<eref target="https://github.com/httpwg/http-core/issues/286"/>)</li>
+   <li>In <xref target="requirements.notation"/>, reference RFC 8174 as well (<eref target="https://github.com/httpwg/http-core/issues/303"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-07" anchor="changes.since.07">
+<ul x:when-empty="None yet.">
+   <li>Throughout, replace "effective request URI", "request-target" and similar with "target URI" (<eref target="https://github.com/httpwg/http-core/issues/259"/>)</li>
+   <li>In <xref target="cache-response-directive.public"/> and <xref target="cache-response-directive.private"/>, make it clear that these directives do not ignore other requirements for caching (<eref target="https://github.com/httpwg/http-core/issues/320"/>)</li>
+   <li>In <xref target="incomplete.responses"/>, move definition of "complete" into semantics (<eref target="https://github.com/httpwg/http-core/issues/334"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-08" anchor="changes.since.08">
+<ul x:when-empty="None yet.">
+   <li><xref target="collected.abnf"/> now uses the sender variant of the "#" list expansion (<eref target="https://github.com/httpwg/http-core/issues/192"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-09" anchor="changes.since.09">
+<ul x:when-empty="None yet.">
+  <li>In <xref target="field.age"/>, discuss handling of invalid and multiple Age header field values (<eref target="https://github.com/httpwg/http-core/issues/193"/>)</li>
+  <li>Switch to xml2rfc v3 mode for draft generation (<eref target="https://github.com/httpwg/http-core/issues/394"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-10" anchor="changes.since.10">
+<ul x:when-empty="None yet.">
+  <li>In <xref target="field.cache-control"/> (<x:ref>Cache-Control</x:ref>), adjust ABNF to allow empty lists (<eref target="https://github.com/httpwg/http-core/issues/210"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-11" anchor="changes.since.11">
+<ul x:when-empty="None.">
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-12" anchor="changes.since.12">
+<ul x:when-empty="None yet.">
+  <li>In <xref target="serving.stale.responses"/>, remove 'no-store', as it won't be in cache in the first place (<eref target="https://github.com/httpwg/http-core/issues/447"/>)</li>
+  <li>In <xref target="storing.fields"/>, make it clear that only response headers need be stored (<eref target="https://github.com/httpwg/http-core/issues/457"/>)</li>
+  <li>Rewrote "Updating Stored Header Fields" <xref target="update"/> (<eref target="https://github.com/httpwg/http-core/issues/458"/>)</li>
+  <li>In <xref target="calculating.freshness.lifetime"/> clarify how to handle invalid and conflicting directives (<eref target="https://github.com/httpwg/http-core/issues/460"/>)</li>
+  <li>In <xref target="validation.response"/>, mention retry of failed validation requests (<eref target="https://github.com/httpwg/http-core/issues/462"/>)</li>
+  <li>In <xref target="validation.response"/>, clarify requirement on storing a full response to a conditional request (<eref target="https://github.com/httpwg/http-core/issues/463"/>)</li>
+  <li>In <xref target="field.age"/>, clarify error handling (<eref target="https://github.com/httpwg/http-core/issues/471"/>)</li>
+  <li>In <xref target="expiration.model"/>, remove spurious "UTC" (<eref target="https://github.com/httpwg/http-core/issues/472"/>)</li>
+  <li>In <xref target="expiration.model"/>, correct the date-related rule names to consider case-insensitive (<eref target="https://github.com/httpwg/http-core/issues/473"/>)</li>
+  <li>In <xref target="history.lists"/>, strengthen recommendation for application caches to pay attention to cache directives (<eref target="https://github.com/httpwg/http-core/issues/474"/>)</li>
+  <li>In <xref target="constructing.responses.from.caches"/>, mention collapsed requests (<eref target="https://github.com/httpwg/http-core/issues/475"/>)</li>
+  <li>In <xref target="invalidation"/>, relax requirements on Content-Location and Location invalidation (<eref target="https://github.com/httpwg/http-core/issues/478"/>)</li>
+  <li>In <xref target="freshening.responses"/>, refine the exceptions to update on a 304 (<eref target="https://github.com/httpwg/http-core/issues/488"/>)</li>
+  <li>Moved table of Cache-Control directives into <xref target="cache.directive.registration"/> (<eref target="https://github.com/httpwg/http-core/issues/506"/>)</li>
+  <li>In <xref target="notation"/>, remove unused core ABNF rules (<eref target="https://github.com/httpwg/http-core/issues/529"/>)</li>
+  <li>Changed to using "payload data" when defining requirements about the data being conveyed within a message, instead of the terms "payload body" or "response body" or "representation body", since they often get confused with the HTTP/1.1 message body (which includes transfer coding) (<eref target="https://github.com/httpwg/http-core/issues/553"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-13" anchor="changes.since.13">
+<ul x:when-empty="None yet.">
+  <li>In <xref target="cache-response-directive.must-revalidate"/>, clarify requirements around generating an error response (<eref target="https://github.com/httpwg/http-core/issues/608"/>)</li>
+  <li>Changed to using "content" instead of "payload" or "payload data" to avoid confusion with the payload of version-specific messaging frames (<eref target="https://github.com/httpwg/http-core/issues/654"/>)</li>
+  <li>In <xref target="freshening.responses"/>, clarify how multiple validators are handled (<eref target="https://github.com/httpwg/http-core/issues/659"/>)</li>
+  <li>In <xref target="age.calculations"/>, <xref target="field.cache-control"/>, and <xref target="cache-response-directive.no-cache"/>, remove notes about very old HTTP/1.0 behaviours (<eref target="https://github.com/httpwg/http-core/issues/660"/>)</li>
+  <li>In <xref target="cache-response-directive.must-understand"/>, modify operation to be more backwards-compatible with existing implementations (<eref target="https://github.com/httpwg/http-core/issues/661"/>)</li>
+  <li>In <xref target="cache.poisoning"/>, cache poisoning can affect private caches too (<eref target="https://github.com/httpwg/http-core/issues/730"/>)</li>
+</ul>
+</section>
+
+<section title="Since draft-ietf-httpbis-cache-14" anchor="changes.since.14">
+<ul x:when-empty="None yet.">
+  <li>Fix subsection ordering in <xref target="cache-response-directive"/> (<eref target="https://github.com/httpwg/http-core/issues/674"/>)</li>
+  <li>In <xref target="caching.overview"/>, define what a cache key is (<eref target="https://github.com/httpwg/http-core/issues/728"/>)</li>
+</ul>
+</section>
+</section>
+
+<section title="Acknowledgments" anchor="acks" numbered="false">
+<t>
+  See <xref target="Semantics-acks"/>.
+</t>
+</section>
+</back>
+</rfc>
diff --git a/test/fixtures/cache-tests/spec/rfc9112.xml b/test/fixtures/cache-tests/spec/rfc9112.xml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/rfc9112.xml
@@ -0,0 +1,2954 @@
+<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type='text/xsl' href='lib/myxml2rfc.xslt'?>
+<?rfc compact="yes"?>
+<?rfc subcompact="no" ?>
+<?rfc linkmailto="no" ?>
+<?rfc editing="no" ?>
+<?rfc comments="yes"?>
+<?rfc inline="yes"?>
+<?rfc rfcedstyle="yes"?>
+<?rfc-ext allow-markup-in-artwork="yes" ?>
+<?rfc-ext map-dfn="none" ?>
+<?rfc-ext html-pretty-print="prettyprint https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"?>
+<?rfc-ext include-references-in-index="yes" ?>
+<?rfc-ext xml2rfc-backend="202007"?>
+<rfc xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:x="http://purl.org/net/xml2rfc/ext" obsoletes="7230" category="std" x:maturity-level="internet" ipr="pre5378Trust200902"  number="9112" seriesNo="99" version="3" tocDepth="4" sortRefs="true">
+<front>
+
+  <title>HTTP/1.1</title>
+
+  <author fullname="Roy T. Fielding" initials="R." surname="Fielding" role="editor">
+    <organization>Adobe</organization>
+    <address>
+      <postal>
+        <postalLine>345 Park Ave</postalLine>
+        <postalLine>San Jose, CA 95110</postalLine>
+        <country>United States of America</country>
+      </postal>
+      <email>fielding@gbiv.com</email>
+      <uri>https://roy.gbiv.com/</uri>
+    </address>
+  </author>
+
+  <author fullname="Mark Nottingham" initials="M." surname="Nottingham" role="editor">
+    <organization>Fastly</organization>
+    <address>
+       <postal>
+         <postalLine>Prahran</postalLine>
+         <country>Australia</country>
+       </postal>
+      <email>mnot@mnot.net</email>
+      <uri>https://www.mnot.net/</uri>
+    </address>
+  </author>
+
+  <author fullname="Julian Reschke" initials="J." surname="Reschke" role="editor">
+    <organization abbrev="greenbytes">greenbytes GmbH</organization>
+    <address>
+      <postal>
+        <postalLine>Hafenweg 16</postalLine>
+        <postalLine>48155 Münster</postalLine>
+        <country>Germany</country>
+      </postal>
+      <email>julian.reschke@greenbytes.de</email>
+      <uri>https://greenbytes.de/tech/webdav/</uri>
+    </address>
+  </author>
+
+  <date year="2022" month="June"/>
+
+  <area>Applications and Real-Time</area>
+  <workgroup>HTTP</workgroup>
+
+  <keyword>Hypertext Transfer Protocol</keyword>
+  <keyword>HTTP</keyword>
+  <keyword>HTTP message format</keyword>
+
+<abstract>
+<t>
+   The Hypertext Transfer Protocol (HTTP) is a stateless application-level
+   protocol for distributed, collaborative, hypertext information systems.
+   This document specifies the HTTP/1.1 message syntax, message parsing,
+   connection management, and related security concerns.
+</t>
+<t>
+   This document obsoletes portions of RFC 7230.
+</t>
+</abstract>
+</front>
+<middle>
+<section title="Introduction" anchor="introduction">
+<t>
+   The Hypertext Transfer Protocol (HTTP) is a stateless application-level
+   request/response protocol that uses extensible semantics and
+   self-descriptive messages for flexible interaction with network-based
+   hypertext information systems. HTTP/1.1 is defined by:
+</t>
+<ul>
+   <li>This document</li>
+   <li>"HTTP Semantics" <xref target="HTTP"/></li>
+   <li>"HTTP Caching" <xref target="CACHING"/></li>
+</ul>
+<t>
+   This document specifies how HTTP semantics are conveyed using the
+   HTTP/1.1 message syntax, framing, and connection management mechanisms.
+   Its goal is to define the complete set of requirements for HTTP/1.1
+   message parsers and message-forwarding intermediaries.
+</t>
+<t>
+   This document obsoletes the portions of
+   <xref target="RFC7230" format="none">RFC 7230</xref> related to HTTP/1.1
+   messaging and connection management, with the changes being summarized in
+   <xref target="changes.from.rfc.7230"/>. The other parts of
+   <xref target="RFC7230" format="none">RFC 7230</xref> are obsoleted by
+   "HTTP Semantics" <xref target="HTTP"/>.
+</t>
+
+<section title="Requirements Notation" anchor="requirements.notation">
+<t>
+   The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL NOT</bcp14>",
+   "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
+   "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
+   described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
+   appear in all capitals, as shown here.
+</t>
+<t>
+   Conformance criteria and considerations regarding error handling
+   are defined in <xref target="HTTP" x:rel="#conformance"/>.
+</t>
+</section>
+
+<section title="Syntax Notation" anchor="notation">
+<iref primary="true" item="Grammar" subitem="ALPHA"/>
+<iref primary="true" item="Grammar" subitem="CR"/>
+<iref primary="true" item="Grammar" subitem="CRLF"/>
+<iref primary="true" item="Grammar" subitem="CTL"/>
+<iref primary="true" item="Grammar" subitem="DIGIT"/>
+<iref primary="true" item="Grammar" subitem="DQUOTE"/>
+<iref primary="true" item="Grammar" subitem="HEXDIG"/>
+<iref primary="true" item="Grammar" subitem="HTAB"/>
+<iref primary="true" item="Grammar" subitem="LF"/>
+<iref primary="true" item="Grammar" subitem="OCTET"/>
+<iref primary="true" item="Grammar" subitem="SP"/>
+<iref primary="true" item="Grammar" subitem="VCHAR"/>
+<t>
+   This specification uses the Augmented Backus-Naur Form (ABNF) notation of
+   <xref target="RFC5234"/>, extended with the notation for case-sensitivity
+   in strings defined in <xref target="RFC7405"/>.
+</t>
+<t>
+   It also uses a list extension, defined in <xref target="HTTP" x:rel="#abnf.extension"/>,
+   that allows for compact definition of comma-separated lists using a "#"
+   operator (similar to how the "*" operator indicates repetition). <xref target="collected.abnf"/> shows the collected grammar with all list
+   operators expanded to standard ABNF notation.
+</t>
+<t>
+   As a convention, ABNF rule names prefixed with "obs-" denote
+   obsolete grammar rules that appear for historical reasons.
+</t>
+<t anchor="core.rules">
+  <x:anchor-alias value="ALPHA"/>
+  <x:anchor-alias value="CR"/>
+  <x:anchor-alias value="CRLF"/>
+  <x:anchor-alias value="CTL"/>
+  <x:anchor-alias value="DIGIT"/>
+  <x:anchor-alias value="DQUOTE"/>
+  <x:anchor-alias value="HEXDIG"/>
+  <x:anchor-alias value="HTAB"/>
+  <x:anchor-alias value="LF"/>
+  <x:anchor-alias value="OCTET"/>
+  <x:anchor-alias value="SP"/>
+  <x:anchor-alias value="VCHAR"/>
+   The following core rules are included by
+   reference, as defined in <xref target="RFC5234" x:fmt="," x:sec="B.1"/>:
+   ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls),
+   DIGIT (decimal 0-9), DQUOTE (double quote),
+   HEXDIG (hexadecimal 0-9/A-F/a-f), HTAB (horizontal tab), LF (line feed),
+   OCTET (any 8-bit sequence of data), SP (space), and
+   VCHAR (any visible <xref target="USASCII"/> character).
+</t>
+<t anchor="imported.rules">
+  <x:anchor-alias value="absolute-path"/>
+  <x:anchor-alias value="comment"/>
+  <x:anchor-alias value="field-name"/>
+  <x:anchor-alias value="field-value"/>
+  <x:anchor-alias value="obs-text"/>
+  <x:anchor-alias value="quoted-string"/>
+  <x:anchor-alias value="token"/>
+  The rules below are defined in <xref target="HTTP"/>:
+</t>
+<sourcecode type="abnf7230">
+  <x:ref>BWS</x:ref>           = &lt;BWS, see <xref target="HTTP" x:rel="#whitespace"/>&gt;
+  <x:ref>OWS</x:ref>           = &lt;OWS, see <xref target="HTTP" x:rel="#whitespace"/>&gt;
+  <x:ref>RWS</x:ref>           = &lt;RWS, see <xref target="HTTP" x:rel="#whitespace"/>&gt;
+  <x:ref>absolute-path</x:ref> = &lt;absolute-path, see <xref target="HTTP" x:rel="#uri.references"/>&gt;
+  <x:ref>field-name</x:ref>    = &lt;field-name, see <xref target="HTTP" x:rel="#fields.names"/>&gt;
+  <x:ref>field-value</x:ref>   = &lt;field-value, see <xref target="HTTP" x:rel="#fields.values"/>&gt;
+  <x:ref>obs-text</x:ref>      = &lt;obs-text, see <xref target="HTTP" x:rel="#quoted.strings"/>&gt;
+  <x:ref>quoted-string</x:ref> = &lt;quoted-string, see <xref target="HTTP" x:rel="#quoted.strings"/>&gt;
+  <x:ref>token</x:ref>         = &lt;token, see <xref target="HTTP" x:rel="#tokens"/>&gt;
+  <x:ref>transfer-coding</x:ref> =
+                  &lt;transfer-coding, see <xref target="HTTP" x:rel="#field.te"/>&gt;
+</sourcecode>
+<t anchor="imported.uri.rules">
+  <x:anchor-alias value="absolute-URI"/>
+  <x:anchor-alias value="authority"/>
+  <x:anchor-alias value="query"/>
+  The rules below are defined in <xref target="URI"/>:
+</t>
+<sourcecode type="abnf7230">
+  <x:ref>absolute-URI</x:ref>  = &lt;absolute-URI, see <xref target="URI" x:fmt="," x:sec="4.3"/>&gt;
+  <x:ref>authority</x:ref>     = &lt;authority, see <xref target="URI" x:fmt="," x:sec="3.2"/>&gt;
+  <x:ref>uri-host</x:ref>      = &lt;host, see <xref target="URI" x:fmt="," x:sec="3.2.2"/>&gt;
+  <x:ref>port</x:ref>          = &lt;port, see <xref target="URI" x:fmt="," x:sec="3.2.3"/>&gt;
+  <x:ref>query</x:ref>         = &lt;query, see <xref target="URI" x:fmt="," x:sec="3.4"/>&gt;
+</sourcecode>
+</section>
+</section>
+
+<section title="Message" anchor="http.message">
+<t>
+   HTTP/1.1 clients and servers communicate by sending messages.
+   See <xref target="HTTP" x:fmt="of" x:rel="#terminology"/> for
+   the general terminology and core concepts of HTTP.
+</t>
+<section title="Message Format" anchor="message.format">
+<x:anchor-alias value="generic-message"/>
+<x:anchor-alias value="message.types"/>
+<x:anchor-alias value="HTTP-message"/>
+<x:anchor-alias value="start-line"/>
+<iref item="header section"/>
+<iref item="headers"/>
+<iref item="header line"/>
+<t>
+   An HTTP/1.1 message consists of a start-line followed by a CRLF and a
+   sequence of
+   octets in a format similar to the Internet Message Format
+   <xref target="RFC5322"/>: zero or more header field lines (collectively
+   referred to as the "headers" or the "header section"), an empty line
+   indicating the end of the header section, and an optional message body.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="HTTP-message"><!--terminal production--></iref>
+  <x:ref>HTTP-message</x:ref>   = <x:ref>start-line</x:ref> <x:ref>CRLF</x:ref>
+                   *( <x:ref>field-line</x:ref> <x:ref>CRLF</x:ref> )
+                   <x:ref>CRLF</x:ref>
+                   [ <x:ref>message-body</x:ref> ]
+</sourcecode>
+<t>
+   A message can be either a request from client to server or a
+   response from server to client.  Syntactically, the two types of messages
+   differ only in the start-line, which is either a request-line (for requests)
+   or a status-line (for responses), and in the algorithm for determining
+   the length of the message body (<xref target="message.body"/>).
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="start-line"/>
+  <x:ref>start-line</x:ref>     = <x:ref>request-line</x:ref> / <x:ref>status-line</x:ref>
+</sourcecode>
+<t>
+   In theory, a client could receive requests and a server could receive
+   responses, distinguishing them by their different start-line formats.
+   In practice, servers are implemented to only expect a request
+   (a response is interpreted as an unknown or invalid request method),
+   and clients are implemented to only expect a response.
+</t>
+<t>
+   HTTP makes use of some protocol elements similar to the
+   Multipurpose Internet Mail Extensions (MIME) <xref target="RFC2045"/>.
+   See <xref target="differences.between.http.and.mime"/> for the
+   differences between HTTP and MIME messages.
+</t>
+</section>
+
+<section title="Message Parsing" anchor="message.parsing">
+<t>
+   The normal procedure for parsing an HTTP message is to read the
+   start-line into a structure, read each header field line into a hash
+   table by field name until the empty line, and then use the parsed
+   data to determine if a message body is expected.  If a message body
+   has been indicated, then it is read as a stream until an amount
+   of octets equal to the message body length is read or the connection
+   is closed.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> parse an HTTP message as a sequence of octets in an
+   encoding that is a superset of US-ASCII <xref target="USASCII"/>.
+   Parsing an HTTP message as a stream of Unicode characters, without regard
+   for the specific encoding, creates security vulnerabilities due to the
+   varying ways that string processing libraries handle invalid multibyte
+   character sequences that contain the octet LF (%x0A).  String-based
+   parsers can only be safely used within protocol elements after the element
+   has been extracted from the message, such as within a header field line value
+   after message parsing has delineated the individual field lines.
+</t>
+<t>
+   Although the line terminator for the start-line and
+   fields is the sequence CRLF, a recipient <bcp14>MAY</bcp14> recognize a
+   single LF as a line terminator and ignore any preceding CR.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate a bare CR (a CR character not immediately
+   followed by LF) within any protocol elements other than the content.
+   A recipient of such a bare CR <bcp14>MUST</bcp14> consider that element to be invalid or
+   replace each bare CR with SP before processing the element or forwarding
+   the message.
+</t>
+<t>
+   Older HTTP/1.0 user agent implementations might send an extra CRLF
+   after a POST request as a workaround for some early server
+   applications that failed to read message body content that was
+   not terminated by a line-ending. An HTTP/1.1 user agent <bcp14>MUST NOT</bcp14>
+   preface or follow a request with an extra CRLF.  If terminating
+   the request message body with a line-ending is desired, then the
+   user agent <bcp14>MUST</bcp14> count the terminating CRLF octets as part of the
+   message body length.
+</t>
+<t>
+   In the interest of robustness, a server that is expecting to receive and
+   parse a request-line <bcp14>SHOULD</bcp14> ignore at least one empty line (CRLF)
+   received prior to the request-line.
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> send whitespace between the start-line and
+   the first header field.
+</t>
+<t>
+   A recipient that receives whitespace between the start-line and
+   the first header field <bcp14>MUST</bcp14> either reject the message as invalid or
+   consume each whitespace-preceded line without further processing of it
+   (i.e., ignore the entire line, along with any subsequent lines preceded
+   by whitespace, until a properly formed header field is received or the
+   header section is terminated).
+   Rejection or removal of invalid whitespace-preceded lines is necessary
+   to prevent their misinterpretation by downstream recipients that might
+   be vulnerable to request smuggling (<xref target="request.smuggling"/>)
+   or response splitting (<xref target="response.splitting"/>) attacks.
+</t>
+<t>
+   When a server listening only for HTTP request messages, or processing
+   what appears from the start-line to be an HTTP request message,
+   receives a sequence of octets that does not match the HTTP-message
+   grammar aside from the robustness exceptions listed above, the
+   server <bcp14>SHOULD</bcp14> respond with a <x:ref>400 (Bad Request)</x:ref> response
+   and close the connection.
+</t>
+</section>
+
+<section title="HTTP Version" anchor="http.version">
+  <x:anchor-alias value="HTTP-version"/>
+  <x:anchor-alias value="HTTP-name"/>
+<t>
+   HTTP uses a "&lt;major&gt;.&lt;minor&gt;" numbering scheme to indicate
+   versions of the protocol. This specification defines version "1.1".
+   <xref target="HTTP" x:rel="#protocol.version"/> specifies the semantics of HTTP version
+   numbers.
+</t>
+<t>
+   The version of an HTTP/1.x message is indicated by an HTTP-version field
+   in the <x:ref>start-line</x:ref>. HTTP-version is case-sensitive.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="HTTP-version"/><iref primary="true" item="Grammar" subitem="HTTP-name"/>
+  <x:ref>HTTP-version</x:ref>  = <x:ref>HTTP-name</x:ref> "/" <x:ref>DIGIT</x:ref> "." <x:ref>DIGIT</x:ref>
+  <x:ref>HTTP-name</x:ref>     = %s"HTTP"
+</sourcecode>
+<t>
+   When an HTTP/1.1 message is sent to an HTTP/1.0 recipient
+   <xref target="HTTP10"/> or a recipient whose version is unknown,
+   the HTTP/1.1 message is constructed such that it can be interpreted
+   as a valid HTTP/1.0 message if all of the newer features are ignored.
+   This specification places recipient-version requirements on some
+   new features so that a conformant sender will only use compatible
+   features until it has determined, through configuration or the
+   receipt of a message, that the recipient supports HTTP/1.1.
+</t>
+<t>
+   Intermediaries that process HTTP messages (i.e., all intermediaries
+   other than those acting as tunnels) <bcp14>MUST</bcp14> send their own HTTP-version
+   in forwarded messages, unless it is purposefully downgraded as a workaround
+   for an upstream issue. In other words, an intermediary is not allowed to blindly
+   forward the <x:ref>start-line</x:ref> without ensuring that the
+   protocol version in that message matches a version to which that
+   intermediary is conformant for both the receiving and
+   sending of messages.  Forwarding an HTTP message without rewriting
+   the HTTP-version might result in communication errors when downstream
+   recipients use the message sender's version to determine what features
+   are safe to use for later communication with that sender.
+</t>
+<t>
+   A server <bcp14>MAY</bcp14> send an HTTP/1.0 response to an HTTP/1.1 request
+   if it is known or suspected that the client incorrectly implements the
+   HTTP specification and is incapable of correctly processing later
+   version responses, such as when a client fails to parse the version
+   number correctly or when an intermediary is known to blindly forward
+   the HTTP-version even when it doesn't conform to the given minor
+   version of the protocol. Such protocol downgrades <bcp14>SHOULD NOT</bcp14> be
+   performed unless triggered by specific client attributes, such as when
+   one or more of the request header fields (e.g., <x:ref>User-Agent</x:ref>)
+   uniquely match the values sent by a client known to be in error.
+</t>
+</section>
+</section>
+
+<section title="Request Line" anchor="request.line">
+  <x:anchor-alias value="Request"/>
+  <x:anchor-alias value="request-line"/>
+<t>
+   A request-line begins with a method token, followed by a single
+   space (SP), the request-target, and another single space (SP), and ends
+   with the protocol version.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="request-line"/>
+  <x:ref>request-line</x:ref>   = <x:ref>method</x:ref> <x:ref>SP</x:ref> <x:ref>request-target</x:ref> <x:ref>SP</x:ref> <x:ref>HTTP-version</x:ref>
+</sourcecode>
+<t>
+   Although the request-line grammar rule requires that each of the component
+   elements be separated by a single SP octet, recipients <bcp14>MAY</bcp14> instead parse
+   on whitespace-delimited word boundaries and, aside from the CRLF
+   terminator, treat any form of whitespace as the SP separator while
+   ignoring preceding or trailing whitespace; such whitespace includes one or
+   more of the following octets: SP, HTAB, VT (%x0B), FF (%x0C), or bare CR.
+   However, lenient parsing can result in request smuggling security
+   vulnerabilities if there are multiple recipients of the message and each
+   has its own unique interpretation of robustness
+   (see <xref target="request.smuggling"/>).
+</t>
+<t>
+   HTTP does not place a predefined limit on the length of a request-line,
+   as described in <xref target="HTTP" x:rel="#length.requirements"/>.
+   A server that receives a method longer than any that it implements
+   <bcp14>SHOULD</bcp14> respond with a <x:ref>501 (Not Implemented)</x:ref> status code.
+   A server that receives a request-target longer than any URI it wishes to
+   parse <bcp14>MUST</bcp14> respond with a
+   <x:ref>414 (URI Too Long)</x:ref> status code (see <xref target="HTTP" x:rel="#status.414"/>).
+</t>
+<t>
+   Various ad hoc limitations on request-line length are found in practice.
+   It is <bcp14>RECOMMENDED</bcp14> that all HTTP senders and recipients support, at a
+   minimum, request-line lengths of 8000 octets.
+</t>
+
+<section title="Method" anchor="request.method">
+  <iref primary="true" item="method"/>
+  <x:anchor-alias value="method"/>
+<t>
+   The method token indicates the request method to be performed on the
+   target resource. The request method is case-sensitive.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="method"/>
+  <x:ref>method</x:ref>         = <x:ref>token</x:ref>
+</sourcecode>
+<t>
+   The request methods defined by this specification can be found in
+   <xref target="HTTP" x:rel="#methods"/>, along with information regarding the HTTP method
+   registry and considerations for defining new methods.
+</t>
+</section>
+
+<section title="Request Target" anchor="request.target">
+  <iref primary="true" item="request-target"/>
+  <x:anchor-alias value="request-target"/>
+<t>
+   The request-target identifies the target resource upon which to apply the
+   request. The client derives a request-target from its desired target URI.
+   There are four distinct formats for the request-target, depending on both
+   the method being requested and whether the request is to a proxy.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="request-target"/><iref primary="false" item="Grammar" subitem="origin-form"/><iref primary="false" item="Grammar" subitem="absolute-form"/><iref primary="false" item="Grammar" subitem="authority-form"/><iref primary="false" item="Grammar" subitem="asterisk-form"/>
+  <x:ref>request-target</x:ref> = <x:ref>origin-form</x:ref>
+                 / <x:ref>absolute-form</x:ref>
+                 / <x:ref>authority-form</x:ref>
+                 / <x:ref>asterisk-form</x:ref>
+</sourcecode>
+<t>
+   No whitespace is allowed in the request-target.
+   Unfortunately, some user agents fail to properly encode or exclude
+   whitespace found in hypertext references, resulting in those disallowed
+   characters being sent as the request-target in a malformed request-line.
+</t>
+<t>
+   Recipients of an invalid request-line <bcp14>SHOULD</bcp14> respond with either a
+   <x:ref>400 (Bad Request)</x:ref> error or a <x:ref>301 (Moved Permanently)</x:ref>
+   redirect with the request-target properly encoded.  A recipient <bcp14>SHOULD NOT</bcp14>
+   attempt to autocorrect and then process the request without a redirect,
+   since the invalid request-line might be deliberately crafted to bypass
+   security filters along the request chain.
+</t>
+<t>
+   A client <bcp14>MUST</bcp14> send a <x:ref>Host</x:ref> header field (<xref target="HTTP" x:rel="#field.host"/>)
+   in all HTTP/1.1 request messages.
+   If the target URI includes an authority component, then a client <bcp14>MUST</bcp14>
+   send a field value for Host that is identical to that authority
+   component, excluding any userinfo subcomponent and its "@" delimiter
+   (<xref target="HTTP" x:rel="#uri.schemes"/>).
+   If the authority component is missing or undefined for the target URI,
+   then a client <bcp14>MUST</bcp14> send a Host header field with an empty field value.
+</t>
+<t>
+   A server <bcp14>MUST</bcp14> respond with a <x:ref>400 (Bad Request)</x:ref> status code
+   to any HTTP/1.1 request message that lacks a Host header field and
+   to any request message that contains more than one Host header field line
+   or a Host header field with an invalid field value.
+</t>
+
+<section title="origin-form" anchor="origin-form">
+   <iref item="origin-form (of request-target)"/>
+<t>
+   The most common form of request-target is the <x:dfn>origin-form</x:dfn>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="origin-form"/>
+  <x:ref>origin-form</x:ref>    = <x:ref>absolute-path</x:ref> [ "?" <x:ref>query</x:ref> ]
+</sourcecode>
+<t>
+   When making a request directly to an origin server, other than a CONNECT
+   or server-wide OPTIONS request (as detailed below),
+   a client <bcp14>MUST</bcp14> send only the absolute path and query components of
+   the target URI as the request-target.
+   If the target URI's path component is empty, the client <bcp14>MUST</bcp14> send
+   "/" as the path within the origin-form of request-target.
+   A <x:ref>Host</x:ref> header field is also sent, as defined in
+   <xref target="HTTP" x:rel="#field.host"/>.
+</t>
+<t>
+   For example, a client wishing to retrieve a representation of the resource
+   identified as
+</t>
+<artwork x:indent-with="  " type="example">
+http://www.example.org/where?q=now
+</artwork>
+<t>
+   directly from the origin server would open (or reuse) a TCP connection
+   to port 80 of the host "www.example.org" and send the lines:
+</t>
+<sourcecode type="http-message">
+GET /where?q=now HTTP/1.1
+Host: www.example.org
+</sourcecode>
+<t>
+   followed by the remainder of the request message.
+</t>
+</section>
+
+<section title="absolute-form" anchor="absolute-form">
+   <iref item="absolute-form (of request-target)"/>
+<t>
+   When making a request to a proxy, other than a CONNECT or server-wide
+   OPTIONS request (as detailed below), a client <bcp14>MUST</bcp14> send the target URI
+   in <x:dfn>absolute-form</x:dfn> as the request-target.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="absolute-form"/>
+  <x:ref>absolute-form</x:ref>  = <x:ref>absolute-URI</x:ref>
+</sourcecode>
+<t>
+   The proxy is requested to either service that request from a valid cache,
+   if possible, or make the same request on the client's behalf either to
+   the next inbound proxy server or directly to the origin server indicated
+   by the request-target.  Requirements on such "forwarding" of messages are
+   defined in <xref target="HTTP" x:rel="#message.forwarding"/>.
+</t>
+<t>
+   An example absolute-form of request-line would be:
+</t>
+<sourcecode type="http-message">
+GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1
+</sourcecode>
+<t>
+   A client <bcp14>MUST</bcp14> send a Host header field in an HTTP/1.1 request even
+   if the request-target is in the absolute-form, since this
+   allows the Host information to be forwarded through ancient HTTP/1.0
+   proxies that might not have implemented Host.
+</t>
+<t>
+   When a proxy receives a request with an absolute-form of
+   request-target, the proxy <bcp14>MUST</bcp14> ignore the received
+   Host header field (if any) and instead replace it with the host
+   information of the request-target.  A proxy that forwards such a request
+   <bcp14>MUST</bcp14> generate a new Host field value based on the received
+   request-target rather than forward the received Host field value.
+</t>
+<t>
+   When an origin server receives a request with an absolute-form of
+   request-target, the origin server <bcp14>MUST</bcp14> ignore the received Host header
+   field (if any) and instead use the host information of the request-target.
+   Note that if the request-target does not have an authority component, an
+   empty Host header field will be sent in this case.
+</t>
+<t>
+   A server <bcp14>MUST</bcp14> accept the absolute-form in requests even though most
+   HTTP/1.1 clients will only send the absolute-form to a proxy.
+</t>
+</section>
+
+<section title="authority-form" anchor="authority-form">
+   <iref item="authority-form (of request-target)"/>
+<t>
+   The <x:dfn>authority-form</x:dfn> of request-target is only used for
+   CONNECT requests (<xref target="HTTP" x:rel="#CONNECT"/>). It consists of only the
+   <x:ref>uri-host</x:ref> and <x:ref>port</x:ref> number of the tunnel
+   destination, separated by a colon (":").
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="authority-form"/>
+  <x:ref>authority-form</x:ref> = <x:ref>uri-host</x:ref> ":" <x:ref>port</x:ref>
+</sourcecode>
+<t>
+   When making a CONNECT request to establish a tunnel through one or more
+   proxies, a client <bcp14>MUST</bcp14> send only the host and port of the tunnel
+   destination as the request-target. The client obtains the host and port
+   from the target URI's <x:ref>authority</x:ref> component, except that it
+   sends the scheme's default port if the target URI elides the port.
+   For example, a CONNECT request to "http://www.example.com" looks like the
+   following:
+</t>
+<sourcecode type="http-message">
+CONNECT www.example.com:80 HTTP/1.1
+Host: www.example.com
+
+</sourcecode>
+</section>
+
+<section title="asterisk-form" anchor="asterisk-form">
+   <iref item="asterisk-form (of request-target)"/>
+<t>
+   The <x:dfn>asterisk-form</x:dfn> of request-target is only used for a server-wide
+   OPTIONS request (<xref target="HTTP" x:rel="#OPTIONS"/>).
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="asterisk-form"/>
+  <x:ref>asterisk-form</x:ref>  = "*"
+</sourcecode>
+<t>
+   When a client wishes to request OPTIONS
+   for the server as a whole, as opposed to a specific named resource of
+   that server, the client <bcp14>MUST</bcp14> send only "*" (%x2A) as the request-target.
+   For example,
+</t>
+<sourcecode type="http-message">
+OPTIONS * HTTP/1.1
+</sourcecode>
+<t>
+   If a proxy receives an OPTIONS request with an absolute-form of
+   request-target in which the URI has an empty path and no query component,
+   then the last proxy on the request chain <bcp14>MUST</bcp14> send a request-target
+   of "*" when it forwards the request to the indicated origin server.
+</t>
+<t>
+   For example, the request
+</t>
+<sourcecode type="http-message">
+OPTIONS http://www.example.org:8001 HTTP/1.1
+</sourcecode>
+<t>
+  would be forwarded by the final proxy as
+</t>
+<sourcecode type="http-message">
+OPTIONS * HTTP/1.1
+Host: www.example.org:8001
+</sourcecode>
+<t>
+   after connecting to port 8001 of host "www.example.org".
+</t>
+</section>
+</section>
+
+<section title="Reconstructing the Target URI" anchor="reconstructing.target.uri">
+  <x:anchor-alias value="h1.effective.request.uri"/>
+<t>
+   The target URI is the <x:ref>request-target</x:ref> when the
+   request-target is in <x:ref>absolute-form</x:ref>. In that case,
+   a server will parse the URI into its generic components for further
+   evaluation.
+</t>
+<t>
+   Otherwise, the server reconstructs the target URI from the connection
+   context and various parts of the request message in order to identify the
+   target resource (<xref target="HTTP" x:rel="#target.resource"/>):
+</t>
+<ul>
+<li>
+   If the server's configuration provides for a fixed URI scheme, or a scheme
+   is provided by a trusted outbound gateway, that scheme is used for the
+   target URI. This is common in large-scale deployments because a gateway
+   server will receive the client's connection context and replace that with
+   their own connection to the inbound server.
+   Otherwise, if the request is received over a secured connection,
+   the target URI's scheme is "https"; if not, the scheme is "http".
+</li>
+<li>
+   If the request-target is in <x:ref>authority-form</x:ref>,
+   the target URI's authority component is the request-target.
+   Otherwise, the target URI's authority component is the field value of the
+   <x:ref>Host</x:ref> header field. If there is no <x:ref>Host</x:ref>
+   header field or if its field value is empty or invalid,
+   the target URI's authority component is empty.
+</li>
+<li>
+   If the request-target is in <x:ref>authority-form</x:ref> or
+   <x:ref>asterisk-form</x:ref>, the target URI's combined
+   <x:ref>path</x:ref> and <x:ref>query</x:ref> component is empty.
+   Otherwise, the target URI's combined <x:ref>path</x:ref> and
+   <x:ref>query</x:ref> component is the request-target.
+</li>
+<li>
+   The components of a reconstructed target URI, once determined as above,
+   can be recombined into <x:ref>absolute-URI</x:ref> form by concatenating
+   the scheme, "://", authority, and combined path and query component.
+</li>
+</ul>
+<t>
+   Example 1: The following message received over a secure connection
+</t>
+<sourcecode type="http-message">
+GET /pub/WWW/TheProject.html HTTP/1.1
+Host: www.example.org
+</sourcecode>
+<t>
+  has a target URI of
+</t>
+<artwork type="example" x:indent-with="  ">
+https://www.example.org/pub/WWW/TheProject.html
+</artwork>
+<t>
+   Example 2: The following message received over an insecure connection
+</t>
+<sourcecode type="http-message">
+OPTIONS * HTTP/1.1
+Host: www.example.org:8080
+</sourcecode>
+<t>
+  has a target URI of
+</t>
+<artwork type="example" x:indent-with="  ">
+http://www.example.org:8080
+</artwork>
+<t>
+   If the target URI's authority component is empty and its URI scheme
+   requires a non-empty authority (as is the case for "http" and "https"),
+   the server can reject the request or determine whether a configured
+   default applies that is consistent with the incoming connection's context.
+   Context might include connection details like address and port, what
+   security has been applied, and locally defined information specific to
+   that server's configuration. An empty authority is replaced with the
+   configured default before further processing of the request.
+</t>
+<t>
+   Supplying a default name for authority within the context of a secured
+   connection is inherently unsafe if there is any chance that the user
+   agent's intended authority might differ from the default.
+   A server that can uniquely identify an authority from the request
+   context <bcp14>MAY</bcp14> use that identity as a default without this risk.
+   Alternatively, it might be better to redirect the request to a safe
+   resource that explains how to obtain a new client.
+</t>
+<t>
+   Note that reconstructing the client's target URI is only half of the
+   process for identifying a target resource. The other half is determining
+   whether that target URI identifies a resource for which the server is
+   willing and able to send a response, as defined in
+   <xref target="HTTP" x:rel="#routing.reject"/>.
+</t>
+</section>
+</section>
+
+<section title="Status Line" anchor="status.line">
+  <x:anchor-alias value="response"/>
+  <x:anchor-alias value="status-line"/>
+  <x:anchor-alias value="status-code"/>
+  <x:anchor-alias value="status code"/>
+  <x:anchor-alias value="reason-phrase"/>
+<t>
+   The first line of a response message is the status-line, consisting
+   of the protocol version, a space (SP), the status code, and another space
+   and ending with an <bcp14>OPTIONAL</bcp14> textual phrase describing the status code.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="status-line"/>
+  <x:ref>status-line</x:ref> = <x:ref>HTTP-version</x:ref> <x:ref>SP</x:ref> <x:ref>status-code</x:ref> <x:ref>SP</x:ref> [ <x:ref>reason-phrase</x:ref> ]
+</sourcecode>
+<t>
+   Although the status-line grammar rule requires that each of the component
+   elements be separated by a single SP octet, recipients <bcp14>MAY</bcp14> instead parse
+   on whitespace-delimited word boundaries and, aside from the line
+   terminator, treat any form of whitespace as the SP separator while
+   ignoring preceding or trailing whitespace; such whitespace includes one or
+   more of the following octets: SP, HTAB, VT (%x0B), FF (%x0C), or bare CR.
+   However, lenient parsing can result in response splitting security
+   vulnerabilities if there are multiple recipients of the message and each
+   has its own unique interpretation of robustness
+   (see <xref target="response.splitting"/>).
+</t>
+<t>
+   The status-code element is a 3-digit integer code describing the
+   result of the server's attempt to understand and satisfy the client's
+   corresponding request. A recipient parses and interprets the remainder
+   of the response message in light of the semantics defined for that
+   status code, if the status code is recognized by that recipient,
+   or in accordance with the class of that status code when the specific
+   code is unrecognized.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="status-code"/>
+  <x:ref>status-code</x:ref>    = 3<x:ref>DIGIT</x:ref>
+</sourcecode>
+<t>
+   HTTP's core status codes are defined in <xref target="HTTP" x:rel="#status.codes"/>,
+   along with the classes of status codes, considerations for the
+   definition of new status codes, and the IANA registry for collecting
+   such definitions.
+</t>
+<t>
+   The reason-phrase element exists for the sole purpose of providing a
+   textual description associated with the numeric status code, mostly out of
+   deference to earlier Internet application protocols that were more
+   frequently used with interactive text clients.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="reason-phrase"/>
+  <x:ref>reason-phrase</x:ref>  = 1*( <x:ref>HTAB</x:ref> / <x:ref>SP</x:ref> / <x:ref>VCHAR</x:ref> / <x:ref>obs-text</x:ref> )
+</sourcecode>
+<t>
+   A client <bcp14>SHOULD</bcp14> ignore the reason-phrase content because it is not a
+   reliable channel for information (it might be translated for a given locale,
+   overwritten by intermediaries, or discarded when the message is forwarded
+   via other versions of HTTP).
+   A server <bcp14>MUST</bcp14> send the space that separates the status-code from the
+   reason-phrase even when the reason-phrase is absent (i.e., the status-line
+   would end with the space).
+</t>
+</section>
+
+<section title="Field Syntax" anchor="header.field.syntax">
+  <x:anchor-alias value="field-line"/>
+<t>
+   Each field line consists of a case-insensitive field name
+   followed by a colon (":"), optional leading whitespace, the field line value,
+   and optional trailing whitespace.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="field-line"/><iref primary="false" item="Grammar" subitem="field-name"/><iref primary="false" item="Grammar" subitem="field-value"/>
+  <x:ref>field-line</x:ref>   = <x:ref>field-name</x:ref> ":" <x:ref>OWS</x:ref> <x:ref>field-value</x:ref> <x:ref>OWS</x:ref>
+</sourcecode>
+<t>
+   Rules for parsing within field values are
+   defined in <xref target="HTTP" x:rel="#fields.values"/>. This section covers the
+   generic syntax for header field inclusion within, and extraction from,
+   HTTP/1.1 messages.
+</t>
+
+<section title="Field Line Parsing" anchor="field.parsing">
+  <x:anchor-alias value="header.parsing"/>
+<t>
+   Messages are parsed using a generic algorithm, independent of the
+   individual field names. The contents within a given field line value are
+   not parsed until a later stage of message interpretation (usually after the
+   message's entire field section has been processed).
+</t>
+<t>
+   No whitespace is allowed between the field name and colon.
+   In the past, differences in the handling of such whitespace have led to
+   security vulnerabilities in request routing and response handling.
+   A server <bcp14>MUST</bcp14> reject, with a response status code of
+   <x:ref>400 (Bad Request)</x:ref>, any received request message that contains
+   whitespace between a header field name and colon.
+   A proxy <bcp14>MUST</bcp14> remove any such whitespace
+   from a response message before forwarding the message downstream.
+</t>
+<t>
+   A field line value might be preceded and/or followed by optional whitespace
+   (OWS); a single SP preceding the field line value is preferred for consistent
+   readability by humans.
+   The field line value does not include that leading or trailing whitespace: OWS
+   occurring before the first non-whitespace octet of the field line value,
+   or after the last non-whitespace octet of the field line value, is excluded by
+   parsers when extracting the field line value from a field line.
+</t>
+</section>
+
+<section title="Obsolete Line Folding" anchor="line.folding">
+  <x:anchor-alias value="obs-fold"/>
+<t>
+   Historically, HTTP/1.x field values could be extended over multiple
+   lines by preceding each extra line with at least one space or horizontal
+   tab (obs-fold). This specification deprecates such line folding except
+   within the "message/http" media type
+   (<xref target="media.type.message.http"/>).
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="obs-fold"/>
+  <x:ref>obs-fold</x:ref>     = <x:ref>OWS</x:ref> <x:ref>CRLF</x:ref> <x:ref>RWS</x:ref>
+               ; obsolete line folding
+</sourcecode>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> generate a message that includes line folding
+   (i.e., that has any field line value that contains a match to the
+   <x:ref>obs-fold</x:ref> rule) unless the message is intended for packaging
+   within the "message/http" media type.
+</t>
+<t>
+   A server that receives an <x:ref>obs-fold</x:ref> in a request message that
+   is not within a "message/http" container <bcp14>MUST</bcp14> either reject the message by
+   sending a <x:ref>400 (Bad Request)</x:ref>, preferably with a
+   representation explaining that obsolete line folding is unacceptable, or
+   replace each received <x:ref>obs-fold</x:ref> with one or more
+   <x:ref>SP</x:ref> octets prior to interpreting the field value or
+   forwarding the message downstream.
+</t>
+<t>
+   A proxy or gateway that receives an <x:ref>obs-fold</x:ref> in a response
+   message that is not within a "message/http" container <bcp14>MUST</bcp14> either discard
+   the message and replace it with a <x:ref>502 (Bad Gateway)</x:ref>
+   response, preferably with a representation explaining that unacceptable
+   line folding was received, or replace each received <x:ref>obs-fold</x:ref>
+   with one or more <x:ref>SP</x:ref> octets prior to interpreting the field
+   value or forwarding the message downstream.
+</t>
+<t>
+   A user agent that receives an <x:ref>obs-fold</x:ref> in a response message
+   that is not within a "message/http" container <bcp14>MUST</bcp14> replace each received
+   <x:ref>obs-fold</x:ref> with one or more <x:ref>SP</x:ref> octets prior to
+   interpreting the field value.
+</t>
+</section>
+</section>
+
+<section title="Message Body" anchor="message.body">
+  <x:anchor-alias value="message-body"/>
+<t>
+   The message body (if any) of an HTTP/1.1 message is used to carry content
+   (<xref target="HTTP" x:rel="#content"/>) for the request or response. The
+   message body is identical to the content unless a transfer coding has
+   been applied, as described in <xref target="field.transfer-encoding"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="message-body"/>
+  <x:ref>message-body</x:ref> = *OCTET
+</sourcecode>
+<t>
+   The rules for determining when a message body is present in an HTTP/1.1
+   message differ for requests and responses.
+</t>
+<t>
+   The presence of a message body in a request is signaled by a
+   <x:ref>Content-Length</x:ref> or <x:ref>Transfer-Encoding</x:ref> header
+   field. Request message framing is independent of method semantics.
+</t>
+<t>
+   The presence of a message body in a response, as detailed in
+   <xref target="message.body.length"/>, depends on both the request
+   method to which it is responding and the response status code. This
+   corresponds to when response content is allowed by HTTP semantics
+   (<xref target="HTTP" x:rel="#content.semantics"/>).
+</t>
+
+<section title="Transfer-Encoding" anchor="field.transfer-encoding">
+  <x:anchor-alias value="header.transfer-encoding"/>
+  <iref primary="true" item="Fields" subitem="Transfer-Encoding" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="Transfer-Encoding" x:for-anchor=""/><iref primary="true" item="Transfer-Encoding header field" x:for-anchor=""/>
+  <iref item="chunked (Coding Format)"/>
+  <x:anchor-alias value="Transfer-Encoding"/>
+<t>
+   The Transfer-Encoding header field lists the transfer coding names
+   corresponding to the sequence of transfer codings that have been
+   (or will be) applied to the content in order to form the message body.
+   Transfer codings are defined in <xref target="transfer.codings"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="Transfer-Encoding"/>
+  <x:ref>Transfer-Encoding</x:ref> = #<x:ref>transfer-coding</x:ref>
+                       ; defined in <xref target="HTTP" x:rel="#field.te"/>
+</sourcecode>
+<t>
+   Transfer-Encoding is analogous to the Content-Transfer-Encoding field of
+   MIME, which was designed to enable safe transport of binary data over a
+   7-bit transport service (<xref target="RFC2045" x:fmt="," x:sec="6"/>).
+   However, safe transport has a different focus for an 8bit-clean transfer
+   protocol. In HTTP's case, Transfer-Encoding is primarily intended to
+   accurately delimit dynamically generated content. It also serves to
+   distinguish encodings that are only applied in transit from the encodings
+   that are a characteristic of the selected representation.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> be able to parse the chunked transfer coding
+   (<xref target="chunked.encoding"/>) because it plays a crucial role in
+   framing messages when the content size is not known in advance.
+   A sender <bcp14>MUST NOT</bcp14> apply the chunked transfer coding more than once to a
+   message body (i.e., chunking an already chunked message is not allowed).
+   If any transfer coding other than chunked is applied to a request's content,
+   the sender <bcp14>MUST</bcp14> apply chunked as the final transfer coding to
+   ensure that the message is properly framed.
+   If any transfer coding other than chunked is applied to a response's content,
+   the sender <bcp14>MUST</bcp14> either apply chunked as the final transfer coding
+   or terminate the message by closing the connection.
+</t>
+<t>
+   For example,
+</t>
+<sourcecode type="http-message">
+Transfer-Encoding: gzip, chunked
+</sourcecode>
+<t>
+   indicates that the content has been compressed using the gzip
+   coding and then chunked using the chunked coding while forming the
+   message body.
+</t>
+<t>
+   Unlike <x:ref>Content-Encoding</x:ref> (<xref target="HTTP" x:rel="#content.codings"/>),
+   Transfer-Encoding is a property of the message, not of the representation.
+   Any recipient along the request/response chain <bcp14>MAY</bcp14> decode the received
+   transfer coding(s) or apply additional transfer coding(s) to the message
+   body, assuming that corresponding changes are made to the Transfer-Encoding
+   field value. Additional information about the encoding parameters can be
+   provided by other header fields not defined by this specification.
+</t>
+<t>
+   Transfer-Encoding <bcp14>MAY</bcp14> be sent in a response to a HEAD request or in a
+   <x:ref>304 (Not Modified)</x:ref> response (<xref target="HTTP" x:rel="#status.304"/>) to a GET request,
+   neither of which includes a message body,
+   to indicate that the origin server would have applied a transfer coding
+   to the message body if the request had been an unconditional GET.
+   This indication is not required, however, because any recipient on
+   the response chain (including the origin server) can remove transfer
+   codings when they are not needed.
+</t>
+<t>
+   A server <bcp14>MUST NOT</bcp14> send a Transfer-Encoding header field in any response
+   with a status code of
+   <x:ref>1xx (Informational)</x:ref> or <x:ref>204 (No Content)</x:ref>.
+   A server <bcp14>MUST NOT</bcp14> send a Transfer-Encoding header field in any
+   <x:ref>2xx (Successful)</x:ref> response to a CONNECT request (<xref target="HTTP" x:rel="#CONNECT"/>).
+</t>
+<t>
+   A server that receives a request message with a transfer coding it does
+   not understand <bcp14>SHOULD</bcp14> respond with <x:ref>501 (Not Implemented)</x:ref>.
+</t>
+<t>
+   Transfer-Encoding was added in HTTP/1.1. It is generally assumed that
+   implementations advertising only HTTP/1.0 support will not understand
+   how to process transfer-encoded content, and that an HTTP/1.0 message
+   received with a Transfer-Encoding is likely to have been forwarded
+   without proper handling of the chunked transfer coding in transit.
+</t>
+<t>
+   A client <bcp14>MUST NOT</bcp14> send a request containing Transfer-Encoding unless it
+   knows the server will handle HTTP/1.1 requests (or later minor revisions);
+   such knowledge might be in the form of specific user configuration or by
+   remembering the version of a prior received response.
+   A server <bcp14>MUST NOT</bcp14> send a response containing Transfer-Encoding unless
+   the corresponding request indicates HTTP/1.1 (or later minor revisions).
+</t>
+<t>
+   Early implementations of Transfer-Encoding would occasionally send both
+   a chunked transfer coding for message framing and an estimated Content-Length
+   header field for use by progress bars. This is why Transfer-Encoding is
+   defined as overriding Content-Length, as opposed to them being mutually
+   incompatible. Unfortunately, forwarding such a message can lead to
+   vulnerabilities regarding
+   request smuggling (<xref target="request.smuggling"/>) or
+   response splitting (<xref target="response.splitting"/>) attacks
+   if any downstream recipient fails to parse the message according to this
+   specification, particularly when a downstream recipient only implements
+   HTTP/1.0.
+</t>
+<t>
+   A server <bcp14>MAY</bcp14> reject a request that contains both Content-Length and
+   Transfer-Encoding or process such a request in accordance with the
+   Transfer-Encoding alone. Regardless, the server <bcp14>MUST</bcp14> close the connection
+   after responding to such a request to avoid the potential attacks.
+</t>
+<t>
+   A server or client that receives an HTTP/1.0 message containing a
+   Transfer-Encoding header field <bcp14>MUST</bcp14> treat the message as if the framing
+   is faulty, even if a Content-Length is present, and close the connection
+   after processing the message. The message sender might have retained a
+   portion of the message, in buffer, that could be misinterpreted by further
+   use of the connection.
+</t>
+</section>
+
+<section title="Content-Length" anchor="body.content-length">
+  <iref primary="false" item="Content-Length header field" x:for-anchor=""/>
+  <x:anchor-alias value="Content-Length"/>
+<t>
+   When a message does not have a <x:ref>Transfer-Encoding</x:ref> header
+   field, a Content-Length header field (<xref target="HTTP" x:rel="#field.content-length"/>) can provide the anticipated size,
+   as a decimal number of octets, for potential content.
+   For messages that do include content, the Content-Length field value
+   provides the framing information necessary for determining where the data
+   (and message) ends.  For messages that do not include content, the
+   Content-Length indicates the size of the selected representation
+   (<xref target="HTTP" x:rel="#field.content-length"/>).
+</t>
+<t>
+   A sender <bcp14>MUST NOT</bcp14> send a Content-Length header field in any message that
+   contains a <x:ref>Transfer-Encoding</x:ref> header field.
+</t>
+<aside>
+  <t>
+   <x:h>Note:</x:h> HTTP's use of Content-Length for message framing differs
+   significantly from the same field's use in MIME, where it is an optional
+   field used only within the "message/external-body" media-type.
+  </t>
+</aside>
+</section>
+
+<section title="Message Body Length" anchor="message.body.length">
+  <iref item="chunked (Coding Format)"/>
+<t>
+   The length of a message body is determined by one of the following
+   (in order of precedence):
+</t>
+<ol>
+  <li><t>
+   Any response to a HEAD request and any response with a
+   <x:ref>1xx (Informational)</x:ref>, <x:ref>204 (No Content)</x:ref>, or
+   <x:ref>304 (Not Modified)</x:ref> status code is always
+   terminated by the first empty line after the header fields, regardless of
+   the header fields present in the message, and thus cannot contain a
+   message body or trailer section.
+  </t></li>
+  <li><t>
+   Any <x:ref>2xx (Successful)</x:ref> response to a CONNECT request implies that the
+   connection will become a tunnel immediately after the empty line that
+   concludes the header fields.  A client <bcp14>MUST</bcp14> ignore any
+   <x:ref>Content-Length</x:ref> or <x:ref>Transfer-Encoding</x:ref> header
+   fields received in such a message.
+  </t></li>
+  <li><t>
+   If a message is received with both a <x:ref>Transfer-Encoding</x:ref>
+   and a <x:ref>Content-Length</x:ref> header field, the Transfer-Encoding
+   overrides the Content-Length. Such a message might indicate an attempt to
+   perform request smuggling (<xref target="request.smuggling"/>) or
+   response splitting (<xref target="response.splitting"/>) and ought to be
+   handled as an error.
+   An intermediary that chooses to forward the message <bcp14>MUST</bcp14> first remove the
+   received Content-Length field and process the Transfer-Encoding
+   (as described below) prior to forwarding the message downstream.
+  </t></li>
+  <li><t>
+   If a <x:ref>Transfer-Encoding</x:ref> header field is present
+   and the chunked transfer coding (<xref target="chunked.encoding"/>)
+   is the final encoding, the message body length is determined by reading
+   and decoding the chunked data until the transfer coding indicates the
+   data is complete.
+  </t>
+  <t>
+   If a <x:ref>Transfer-Encoding</x:ref> header field is present in a
+   response and the chunked transfer coding is not the final encoding, the
+   message body length is determined by reading the connection until it is
+   closed by the server.
+  </t>
+  <t>
+   If a <x:ref>Transfer-Encoding</x:ref> header field is present in a request
+   and the chunked transfer coding is not the final encoding, the message body
+   length cannot be determined reliably; the server <bcp14>MUST</bcp14> respond with
+   the <x:ref>400 (Bad Request)</x:ref> status code and then close the
+   connection.
+  </t></li>
+  <li><t>
+   If a message is received without <x:ref>Transfer-Encoding</x:ref> and with
+   an invalid <x:ref>Content-Length</x:ref> header field, then the message
+   framing is invalid and the recipient <bcp14>MUST</bcp14> treat it as an unrecoverable
+   error, unless the field value can be successfully parsed as a
+   comma-separated list (<xref target="HTTP" x:rel="#abnf.extension"/>), all values in the
+   list are valid, and all values in the list are the same (in which case, the
+   message is processed with that single value used as the Content-Length field
+   value).
+   If the unrecoverable error is in a request message,
+   the server <bcp14>MUST</bcp14> respond with
+   a <x:ref>400 (Bad Request)</x:ref> status code and then close the connection.
+   If it is in a response message received by a proxy,
+   the proxy <bcp14>MUST</bcp14> close the connection to the server, discard the received
+   response, and send a <x:ref>502 (Bad Gateway)</x:ref> response to the
+   client.
+   If it is in a response message received by a user agent,
+   the user agent <bcp14>MUST</bcp14> close the connection to the server and discard the
+   received response.
+  </t></li>
+  <li><t>
+   If a valid <x:ref>Content-Length</x:ref> header field is present without
+   <x:ref>Transfer-Encoding</x:ref>, its decimal value defines the
+   expected message body length in octets.
+   If the sender closes the connection or the recipient times out before the
+   indicated number of octets are received, the recipient <bcp14>MUST</bcp14> consider
+   the message to be incomplete and close the connection.
+  </t></li>
+  <li><t>
+   If this is a request message and none of the above are true, then the
+   message body length is zero (no message body is present).
+  </t></li>
+  <li><t>
+   Otherwise, this is a response message without a declared message body
+   length, so the message body length is determined by the number of octets
+   received prior to the server closing the connection.
+  </t></li>
+</ol>
+<t>
+   Since there is no way to distinguish a successfully completed,
+   close-delimited response message from a partially received message interrupted
+   by network failure, a server <bcp14>SHOULD</bcp14> generate encoding or
+   length-delimited messages whenever possible.  The close-delimiting
+   feature exists primarily for backwards compatibility with HTTP/1.0.
+</t>
+<aside>
+  <t>
+     <x:h>Note:</x:h> Request messages are never close-delimited because they are always
+     explicitly framed by length or transfer coding, with the absence of both implying
+     the request ends immediately after the header section.
+  </t>
+</aside>
+<t>
+   A server <bcp14>MAY</bcp14> reject a request that contains a message body but
+   not a <x:ref>Content-Length</x:ref> by responding with
+   <x:ref>411 (Length Required)</x:ref>.
+</t>
+<t>
+   Unless a transfer coding other than chunked has been applied,
+   a client that sends a request containing a message body <bcp14>SHOULD</bcp14>
+   use a valid <x:ref>Content-Length</x:ref> header field if the message body
+   length is known in advance, rather than the chunked transfer coding, since some
+   existing services respond to chunked with a <x:ref>411 (Length Required)</x:ref>
+   status code even though they understand the chunked transfer coding.  This
+   is typically because such services are implemented via a gateway that
+   requires a content length in advance of being called, and the server
+   is unable or unwilling to buffer the entire request before processing.
+</t>
+<t>
+   A user agent that sends a request that contains a message body <bcp14>MUST</bcp14> send
+   either a valid <x:ref>Content-Length</x:ref> header field or use the
+   chunked transfer coding. A client <bcp14>MUST NOT</bcp14> use the chunked transfer
+   coding unless it knows the server will handle HTTP/1.1 (or later)
+   requests; such knowledge can be in the form of specific user configuration
+   or by remembering the version of a prior received response.
+</t>
+<t>
+   If the final response to the last request on a connection has been
+   completely received and there remains additional data to read, a user agent
+   <bcp14>MAY</bcp14> discard the remaining data or attempt to determine if that data
+   belongs as part of the prior message body, which might be the case if the
+   prior message's Content-Length value is incorrect. A client <bcp14>MUST NOT</bcp14>
+   process, cache, or forward such extra data as a separate response, since
+   such behavior would be vulnerable to cache poisoning.
+</t>
+</section>
+</section>
+
+<section title="Transfer Codings" anchor="transfer.codings">
+<t>
+   Transfer coding names are used to indicate an encoding
+   transformation that has been, can be, or might need to be applied to a
+   message's content in order to ensure "safe transport" through the network.
+   This differs from a content coding in that the transfer coding is a
+   property of the message rather than a property of the representation
+   that is being transferred.
+</t>
+<t>
+   All transfer-coding names are case-insensitive and ought to be registered
+   within the HTTP Transfer Coding registry, as defined in
+   <xref target="transfer.coding.registry"/>.
+   They are used in the <x:ref>Transfer-Encoding</x:ref>
+   (<xref target="field.transfer-encoding"/>) and <x:ref>TE</x:ref>
+   (<xref target="HTTP" x:rel="#field.te"/>) header fields (the latter also
+   defining the "transfer-coding" grammar).
+</t>
+
+<section title="Chunked Transfer Coding" anchor="chunked.encoding">
+  <iref primary="true" item="chunked (transfer coding)"/>
+  <x:anchor-alias value="chunk"/>
+  <x:anchor-alias value="chunked-body"/>
+  <x:anchor-alias value="chunk-data"/>
+  <x:anchor-alias value="chunk-size"/>
+  <x:anchor-alias value="last-chunk"/>
+<t>
+   The chunked transfer coding wraps content in order to transfer it
+   as a series of chunks, each with its own size indicator, followed by an
+   <bcp14>OPTIONAL</bcp14> trailer section containing trailer fields. Chunked enables content
+   streams of unknown size to be transferred as a sequence of length-delimited
+   buffers, which enables the sender to retain connection persistence and the
+   recipient to know when it has received the entire message.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="chunked-body"><!--terminal production--></iref><iref primary="true" item="Grammar" subitem="chunk"/><iref primary="true" item="Grammar" subitem="chunk-size"/><iref primary="true" item="Grammar" subitem="last-chunk"/><iref primary="false" item="Grammar" subitem="trailer-section"/><iref primary="false" item="Grammar" subitem="chunk-ext"/><iref primary="true" item="Grammar" subitem="chunk-data"/>
+  <x:ref>chunked-body</x:ref>   = *<x:ref>chunk</x:ref>
+                   <x:ref>last-chunk</x:ref>
+                   <x:ref>trailer-section</x:ref>
+                   <x:ref>CRLF</x:ref>
+
+  <x:ref>chunk</x:ref>          = <x:ref>chunk-size</x:ref> [ <x:ref>chunk-ext</x:ref> ] <x:ref>CRLF</x:ref>
+                   <x:ref>chunk-data</x:ref> <x:ref>CRLF</x:ref>
+  <x:ref>chunk-size</x:ref>     = 1*<x:ref>HEXDIG</x:ref>
+  <x:ref>last-chunk</x:ref>     = 1*("0") [ <x:ref>chunk-ext</x:ref> ] <x:ref>CRLF</x:ref>
+
+  <x:ref>chunk-data</x:ref>     = 1*<x:ref>OCTET</x:ref> ; a sequence of chunk-size octets
+</sourcecode>
+<t>
+   The chunk-size field is a string of hex digits indicating the size of
+   the chunk-data in octets. The chunked transfer coding is complete when a
+   chunk with a chunk-size of zero is received, possibly followed by a
+   trailer section, and finally terminated by an empty line.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> be able to parse and decode the chunked transfer coding.
+</t>
+<t>
+   HTTP/1.1 does not define any means to limit the size of a
+   chunked response such that an intermediary can be assured of buffering the
+   entire response. Additionally, very large chunk sizes may cause overflows
+   or loss of precision if their values are not represented accurately in a
+   receiving implementation. Therefore, recipients <bcp14>MUST</bcp14> anticipate
+   potentially large hexadecimal numerals and prevent parsing errors due to
+   integer conversion overflows or precision loss due to integer
+   representation.
+</t>
+<t>
+   The chunked coding does not define any parameters. Their presence
+   <bcp14>SHOULD</bcp14> be treated as an error.
+</t>
+
+<section title="Chunk Extensions" anchor="chunked.extension">
+  <x:anchor-alias value="chunk-ext"/>
+  <x:anchor-alias value="chunk-ext-name"/>
+  <x:anchor-alias value="chunk-ext-val"/>
+<t>
+   The chunked coding allows each chunk to include zero or more chunk
+   extensions, immediately following the <x:ref>chunk-size</x:ref>, for the
+   sake of supplying per-chunk metadata (such as a signature or hash),
+   mid-message control information, or randomization of message body size.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="chunk-ext"/><iref primary="true" item="Grammar" subitem="chunk-ext-name"/><iref primary="true" item="Grammar" subitem="chunk-ext-val"/>
+  <x:ref>chunk-ext</x:ref>      = *( <x:ref>BWS</x:ref> ";" <x:ref>BWS</x:ref> <x:ref>chunk-ext-name</x:ref>
+                      [ <x:ref>BWS</x:ref> "=" <x:ref>BWS</x:ref> <x:ref>chunk-ext-val</x:ref> ] )
+
+  <x:ref>chunk-ext-name</x:ref> = <x:ref>token</x:ref>
+  <x:ref>chunk-ext-val</x:ref>  = <x:ref>token</x:ref> / <x:ref>quoted-string</x:ref>
+</sourcecode>
+<t>
+   The chunked coding is specific to each connection and is likely to be
+   removed or recoded by each recipient (including intermediaries) before any
+   higher-level application would have a chance to inspect the extensions.
+   Hence, the use of chunk extensions is generally limited to specialized HTTP
+   services such as "long polling" (where client and server can have shared
+   expectations regarding the use of chunk extensions) or for padding within
+   an end-to-end secured connection.
+</t>
+<t>
+   A recipient <bcp14>MUST</bcp14> ignore unrecognized chunk extensions.
+   A server ought to limit the total length of chunk extensions received in a
+   request to an amount reasonable for the services provided, in the same way
+   that it applies length limitations and timeouts for other parts of a
+   message, and generate an appropriate <x:ref>4xx (Client Error)</x:ref>
+   response if that amount is exceeded.
+</t>
+</section>
+
+<section title="Chunked Trailer Section" anchor="chunked.trailer.section">
+  <x:anchor-alias value="trailer-section"/>
+<t>
+   A trailer section allows the sender to include additional fields at the end
+   of a chunked message in order to supply metadata that might be dynamically
+   generated while the content is sent, such as a message integrity
+   check, digital signature, or post-processing status. The proper use and
+   limitations of trailer fields are defined in <xref target="HTTP" x:rel="#trailer.fields"/>.
+</t>
+<sourcecode type="abnf7230"><iref primary="true" item="Grammar" subitem="trailer-section"/><iref primary="false" item="Grammar" subitem="field-line"/>
+  <x:ref>trailer-section</x:ref>   = *( <x:ref>field-line</x:ref> <x:ref>CRLF</x:ref> )
+</sourcecode>
+<t>
+   A recipient that removes the chunked coding from a message <bcp14>MAY</bcp14>
+   selectively retain or discard the received trailer fields. A recipient
+   that retains a received trailer field <bcp14>MUST</bcp14> either store/forward the
+   trailer field separately from the received header fields or merge the
+   received trailer field into the header section.
+   A recipient <bcp14>MUST NOT</bcp14> merge a received trailer field into the
+   header section unless its corresponding header field definition
+   explicitly permits and instructs how the trailer field value can be
+   safely merged.
+</t>
+</section>
+
+<section title="Decoding Chunked" anchor="decoding.chunked">
+<t>
+   A process for decoding the chunked transfer coding
+   can be represented in pseudo-code as:
+</t>
+<sourcecode type="pseudocode">
+  length := 0
+  read chunk-size, chunk-ext (if any), and CRLF
+  while (chunk-size &gt; 0) {
+     read chunk-data and CRLF
+     append chunk-data to content
+     length := length + chunk-size
+     read chunk-size, chunk-ext (if any), and CRLF
+  }
+  read trailer field
+  while (trailer field is not empty) {
+     if (trailer fields are stored/forwarded separately) {
+         append trailer field to existing trailer fields
+     }
+     else if (trailer field is understood and defined as mergeable) {
+         merge trailer field with existing header fields
+     }
+     else {
+         discard trailer field
+     }
+     read trailer field
+  }
+  Content-Length := length
+  Remove "chunked" from Transfer-Encoding
+</sourcecode>
+</section>
+</section>
+
+<section title="Transfer Codings for Compression" anchor="compression.codings">
+  <iref primary="true" item="compress (transfer coding)"/>
+  <iref primary="true" item="x-compress (transfer coding)"/>
+  <iref primary="true" item="deflate (transfer coding)"/>
+  <iref primary="true" item="gzip (transfer coding)"/>
+  <iref primary="true" item="x-gzip (transfer coding)"/>
+<t>
+   The following transfer coding names for compression are defined by
+   the same algorithm as their corresponding content coding:
+</t>
+<dl newline="true">
+  <dt>compress (and x-compress)</dt>
+  <dd>See <xref target="HTTP" x:rel="#compress.coding"/>.</dd>
+  <dt>deflate</dt>
+  <dd>See <xref target="HTTP" x:rel="#deflate.coding"/>.</dd>
+  <dt>gzip (and x-gzip)</dt>
+  <dd>See <xref target="HTTP" x:rel="#gzip.coding"/>.</dd>
+</dl>
+<t>
+   The compression codings do not define any parameters. The presence
+   of parameters with any of these compression codings <bcp14>SHOULD</bcp14> be treated
+   as an error.
+</t>
+</section>
+
+<section title="Transfer Coding Registry" anchor="transfer.coding.registry">
+<t>
+   The "HTTP Transfer Coding Registry" defines the namespace for transfer
+   coding names. It is maintained at <eref target="https://www.iana.org/assignments/http-parameters"/>.
+</t>
+<t>
+   Registrations <bcp14>MUST</bcp14> include the following fields:
+</t>
+<ul>
+  <li>Name</li>
+  <li>Description</li>
+  <li>Pointer to specification text</li>
+</ul>
+<t>
+   Names of transfer codings <bcp14>MUST NOT</bcp14> overlap with names of content codings
+   (<xref target="HTTP" x:rel="#content.codings"/>) unless the encoding transformation is identical, as
+   is the case for the compression codings defined in
+   <xref target="compression.codings"/>.
+</t>
+<t>
+   The <x:ref>TE</x:ref> header field (<xref target="HTTP" x:rel="#field.te"/>) uses a
+   pseudo-parameter named "q" as the rank value when multiple transfer codings
+   are acceptable. Future registrations of transfer codings <bcp14>SHOULD NOT</bcp14>
+   define parameters called "q" (case-insensitively) in order to avoid
+   ambiguities.
+</t>
+<t>
+   Values to be added to this namespace require IETF Review (see
+   <xref target="RFC8126" x:fmt="of" x:sec="4.8"/>) and <bcp14>MUST</bcp14>
+   conform to the purpose of transfer coding defined in this specification.
+</t>
+<t>
+   Use of program names for the identification of encoding formats
+   is not desirable and is discouraged for future encodings.
+</t>
+</section>
+
+<section title="Negotiating Transfer Codings" anchor="transfer.coding.negotiation">
+<t>
+  The TE field (<xref target="HTTP" x:rel="#field.te"/>) is used in HTTP/1.1 to indicate
+  what transfer codings, besides chunked, the client is willing to accept
+  in the response and whether the client is willing to preserve
+  trailer fields in a chunked transfer coding.
+</t>
+<t>
+   A client <bcp14>MUST NOT</bcp14> send the chunked transfer coding name in TE;
+   chunked is always acceptable for HTTP/1.1 recipients.
+</t>
+<t>
+   Three examples of TE use are below.
+</t>
+<sourcecode type="http-message">
+TE: deflate
+TE:
+TE: trailers, deflate;q=0.5
+</sourcecode>
+<t>
+   When multiple transfer codings are acceptable, the client <bcp14>MAY</bcp14> rank the
+   codings by preference using a case-insensitive "q" parameter (similar to
+   the qvalues used in content negotiation fields; see <xref target="HTTP" x:rel="#quality.values"/>). The rank value
+   is a real number in the range 0 through 1, where 0.001 is the least
+   preferred and 1 is the most preferred; a value of 0 means "not acceptable".
+</t>
+<t>
+   If the TE field value is empty or if no TE field is present, the only
+   acceptable transfer coding is chunked. A message with no transfer coding
+   is always acceptable.
+</t>
+<t>
+   The keyword "trailers" indicates that the sender will not discard trailer
+   fields, as described in <xref target="HTTP" x:rel="#trailer.fields"/>.
+</t>
+<t>
+   Since the TE header field only applies to the immediate connection,
+   a sender of TE <bcp14>MUST</bcp14> also send a "TE" connection option within the
+   <x:ref>Connection</x:ref> header field (<xref target="HTTP" x:rel="#field.connection"/>)
+   in order to prevent the TE header field from being forwarded by intermediaries
+   that do not support its semantics.
+</t>
+</section>
+</section>
+
+<section title="Handling Incomplete Messages" anchor="incomplete.messages">
+<t>
+   A server that receives an incomplete request message, usually due to a
+   canceled request or a triggered timeout exception, <bcp14>MAY</bcp14> send an error
+   response prior to closing the connection.
+</t>
+<t>
+   A client that receives an incomplete response message, which can occur
+   when a connection is closed prematurely or when decoding a supposedly
+   chunked transfer coding fails, <bcp14>MUST</bcp14> record the message as incomplete.
+   Cache requirements for incomplete responses are defined in
+   <xref target="CACHING" x:rel="#incomplete.responses"/>.
+</t>
+<t>
+   If a response terminates in the middle of the header section (before the
+   empty line is received) and the status code might rely on header fields to
+   convey the full meaning of the response, then the client cannot assume
+   that meaning has been conveyed; the client might need to repeat the
+   request in order to determine what action to take next.
+</t>
+<t>
+   A message body that uses the chunked transfer coding is
+   incomplete if the zero-sized chunk that terminates the encoding has not
+   been received.  A message that uses a valid <x:ref>Content-Length</x:ref> is
+   incomplete if the size of the message body received (in octets) is less than
+   the value given by Content-Length.  A response that has neither chunked
+   transfer coding nor Content-Length is terminated by closure of the
+   connection and, if the header section was received intact, is considered
+   complete unless an error was indicated by the underlying connection
+   (e.g., an "incomplete close" in TLS would leave the response incomplete,
+   as described in <xref target="tls.connection.closure"/>).
+</t>
+</section>
+
+<section title="Connection Management" anchor="connection.management">
+<t>
+   HTTP messaging is independent of the underlying transport- or
+   session-layer connection protocol(s).  HTTP only presumes a reliable
+   transport with in-order delivery of requests and the corresponding
+   in-order delivery of responses.  The mapping of HTTP request and
+   response structures onto the data units of an underlying transport
+   protocol is outside the scope of this specification.
+</t>
+<t>
+   As described in <xref target="HTTP" x:rel="#routing.inbound"/>, the specific
+   connection protocols to be used for an HTTP interaction are determined by
+   client configuration and the <x:ref>target URI</x:ref>.
+   For example, the "http" URI scheme
+   (<xref target="HTTP" x:rel="#http.uri"/>) indicates a default connection of TCP
+   over IP, with a default TCP port of 80, but the client might be
+   configured to use a proxy via some other connection, port, or protocol.
+</t>
+<t>
+   HTTP implementations are expected to engage in connection management,
+   which includes maintaining the state of current connections,
+   establishing a new connection or reusing an existing connection,
+   processing messages received on a connection, detecting connection
+   failures, and closing each connection.
+   Most clients maintain multiple connections in parallel, including
+   more than one connection per server endpoint.
+   Most servers are designed to maintain thousands of concurrent connections,
+   while controlling request queues to enable fair use and detect
+   denial-of-service attacks.
+</t>
+
+<section title="Establishment" anchor="persistent.establishment">
+<t>
+   It is beyond the scope of this specification to describe how connections
+   are established via various transport- or session-layer protocols.
+   Each HTTP connection maps to one underlying transport connection.
+</t>
+</section>
+
+<section title="Associating a Response to a Request" anchor="associating.response.to.request">
+<t>
+   HTTP/1.1 does not include a request identifier for associating a given
+   request message with its corresponding one or more response messages.
+   Hence, it relies on the order of response arrival to correspond exactly
+   to the order in which requests are made on the same connection.
+   More than one response message per request only occurs when one or more
+   informational responses (<x:ref>1xx</x:ref>; see <xref target="HTTP" x:rel="#status.1xx"/>) precede a
+   final response to the same request.
+</t>
+<t>
+   A client that has more than one outstanding request on a connection <bcp14>MUST</bcp14>
+   maintain a list of outstanding requests in the order sent and <bcp14>MUST</bcp14>
+   associate each received response message on that connection to the
+   first outstanding request that has not yet received a final
+   (non-<x:ref>1xx</x:ref>) response.
+</t>
+<t>
+   If a client receives data on a connection that doesn't have
+   outstanding requests, the client <bcp14>MUST NOT</bcp14> consider that data to be a
+   valid response; the client <bcp14>SHOULD</bcp14> close the connection, since message
+   delimitation is now ambiguous, unless the data consists only of one or
+   more CRLF (which can be discarded per <xref target="message.parsing"/>).
+</t>
+</section>
+
+<section title="Persistence" anchor="persistent.connections">
+   <x:anchor-alias value="persistent connections"/>
+   <iref primary="false" item="close" x:for-anchor=""/>
+<t>
+   HTTP/1.1 defaults to the use of <x:dfn>persistent connections</x:dfn>,
+   allowing multiple requests and responses to be carried over a single
+   connection. HTTP implementations <bcp14>SHOULD</bcp14> support persistent connections.
+</t>
+<t>
+   A recipient determines whether a connection is persistent or not based on
+   the protocol version and <x:ref>Connection</x:ref> header field
+   (<xref target="HTTP" x:rel="#field.connection"/>) in the
+   most recently received message, if any:
+</t>
+<ul>
+   <li>If the "<x:ref>close</x:ref>" connection option is present
+       (<xref target="persistent.tear-down"/>), the
+       connection will not persist after the current response; else,</li>
+   <li>If the received protocol is HTTP/1.1 (or later), the connection will
+       persist after the current response; else,</li>
+   <li>If the received protocol is HTTP/1.0, the "keep-alive" connection
+       option is present, either the recipient is not a proxy or the
+       message is a response, and the recipient wishes to honor the
+       HTTP/1.0 "keep-alive" mechanism, the connection will persist after
+       the current response; otherwise,</li>
+   <li>The connection will close after the current response.</li>
+</ul>
+<t>
+   A client that does not support <x:ref>persistent connections</x:ref> <bcp14>MUST</bcp14>
+   send the "<x:ref>close</x:ref>" connection option in every request message.
+</t>
+<t>
+   A server that does not support <x:ref>persistent connections</x:ref> <bcp14>MUST</bcp14>
+   send the "<x:ref>close</x:ref>" connection option in every response message
+   that does not have a <x:ref>1xx (Informational)</x:ref> status code.
+</t>
+<t>
+   A client <bcp14>MAY</bcp14> send additional requests on a persistent connection until it
+   sends or receives a "<x:ref>close</x:ref>" connection option or receives an
+   HTTP/1.0 response without a "keep-alive" connection option.
+</t>
+<t>
+   In order to remain persistent, all messages on a connection need to
+   have a self-defined message length (i.e., one not defined by closure
+   of the connection), as described in <xref target="message.body"/>.
+   A server <bcp14>MUST</bcp14> read the entire request message body or close
+   the connection after sending its response; otherwise, the
+   remaining data on a persistent connection would be misinterpreted
+   as the next request.  Likewise,
+   a client <bcp14>MUST</bcp14> read the entire response message body if it intends
+   to reuse the same connection for a subsequent request.
+</t>
+<t>
+   A proxy server <bcp14>MUST NOT</bcp14> maintain a persistent connection with an
+   HTTP/1.0 client (see <xref target="compatibility.with.http.1.0.persistent.connections"/> for
+   information and discussion of the problems with the Keep-Alive header field
+   implemented by many HTTP/1.0 clients).
+</t>
+<t>
+   See <xref target="compatibility.with.http.1.0.persistent.connections"/>
+   for more information on backwards compatibility with HTTP/1.0 clients.
+</t>
+
+<section title="Retrying Requests" anchor="persistent.retrying.requests">
+<t>
+   Connections can be closed at any time, with or without intention.
+   Implementations ought to anticipate the need to recover
+   from asynchronous close events. The conditions under which a client can
+   automatically retry a sequence of outstanding requests are defined in
+   <xref target="HTTP" x:rel="#idempotent.methods"/>.
+ </t>
+</section>
+
+<section title="Pipelining" anchor="pipelining">
+   <x:anchor-alias value="pipeline"/>
+<t>
+   A client that supports persistent connections <bcp14>MAY</bcp14> <x:dfn>pipeline</x:dfn>
+   its requests (i.e., send multiple requests without waiting for each
+   response). A server <bcp14>MAY</bcp14> process a sequence of pipelined requests in
+   parallel if they all have safe methods (<xref target="HTTP" x:rel="#safe.methods"/>), but it <bcp14>MUST</bcp14> send
+   the corresponding responses in the same order that the requests were
+   received.
+</t>
+<t>
+   A client that pipelines requests <bcp14>SHOULD</bcp14> retry unanswered requests if the
+   connection closes before it receives all of the corresponding responses.
+   When retrying pipelined requests after a failed connection (a connection
+   not explicitly closed by the server in its last complete response), a
+   client <bcp14>MUST NOT</bcp14> pipeline immediately after connection establishment,
+   since the first remaining request in the prior pipeline might have caused
+   an error response that can be lost again if multiple requests are sent on a
+   prematurely closed connection (see the TCP reset problem described in
+   <xref target="persistent.tear-down"/>).
+</t>
+<t>
+   Idempotent methods (<xref target="HTTP" x:rel="#idempotent.methods"/>) are significant to pipelining
+   because they can be automatically retried after a connection failure.
+   A user agent <bcp14>SHOULD NOT</bcp14> pipeline requests after a non-idempotent method,
+   until the final response status code for that method has been received,
+   unless the user agent has a means to detect and recover from partial
+   failure conditions involving the pipelined sequence.
+</t>
+<t>
+   An intermediary that receives pipelined requests <bcp14>MAY</bcp14> pipeline those
+   requests when forwarding them inbound, since it can rely on the outbound
+   user agent(s) to determine what requests can be safely pipelined. If the
+   inbound connection fails before receiving a response, the pipelining
+   intermediary <bcp14>MAY</bcp14> attempt to retry a sequence of requests that have yet
+   to receive a response if the requests all have idempotent methods;
+   otherwise, the pipelining intermediary <bcp14>SHOULD</bcp14> forward any received
+   responses and then close the corresponding outbound connection(s) so that
+   the outbound user agent(s) can recover accordingly.
+</t>
+</section>
+</section>
+
+<section title="Concurrency" anchor="persistent.concurrency">
+<t>
+   A client ought to limit the number of simultaneous open
+   connections that it maintains to a given server.
+</t>
+<t>
+   Previous revisions of HTTP gave a specific number of connections as a
+   ceiling, but this was found to be impractical for many applications. As a
+   result, this specification does not mandate a particular maximum number of
+   connections but, instead, encourages clients to be conservative when opening
+   multiple connections.
+</t>
+<t>
+   Multiple connections are typically used to avoid the "head-of-line
+   blocking" problem, wherein a request that takes significant server-side
+   processing and/or transfers very large content would block subsequent
+   requests on the
+   same connection. However, each connection consumes server resources.
+</t>
+<t>
+   Furthermore, using multiple connections can cause undesirable side effects
+   in congested networks.
+   Using larger numbers of connections can also cause side effects in
+   otherwise uncongested networks, because their aggregate and initially
+   synchronized sending behavior can cause congestion that would not have
+   been present if fewer parallel connections had been used.
+</t>
+<t>
+   Note that a server might reject traffic that it deems abusive or
+   characteristic of a denial-of-service attack, such as an excessive number
+   of open connections from a single client.
+</t>
+</section>
+
+<section title="Failures and Timeouts" anchor="persistent.failures">
+<t>
+   Servers will usually have some timeout value beyond which they will
+   no longer maintain an inactive connection. Proxy servers might make
+   this a higher value since it is likely that the client will be making
+   more connections through the same proxy server. The use of persistent
+   connections places no requirements on the length (or existence) of
+   this timeout for either the client or the server.
+</t>
+<t>
+   A client or server that wishes to time out <bcp14>SHOULD</bcp14> issue a graceful close
+   on the connection. Implementations <bcp14>SHOULD</bcp14> constantly monitor open
+   connections for a received closure signal and respond to it as appropriate,
+   since prompt closure of both sides of a connection enables allocated system
+   resources to be reclaimed.
+</t>
+<t>
+   A client, server, or proxy <bcp14>MAY</bcp14> close the transport connection at any
+   time. For example, a client might have started to send a new request
+   at the same time that the server has decided to close the "idle"
+   connection. From the server's point of view, the connection is being
+   closed while it was idle, but from the client's point of view, a
+   request is in progress.
+</t>
+<t>
+   A server <bcp14>SHOULD</bcp14> sustain persistent connections, when possible, and allow
+   the underlying transport's flow-control mechanisms to resolve temporary overloads rather
+   than terminate connections with the expectation that clients will retry.
+   The latter technique can exacerbate network congestion or server load.
+</t>
+<t>
+   A client sending a message body <bcp14>SHOULD</bcp14> monitor
+   the network connection for an error response while it is transmitting
+   the request. If the client sees a response that indicates the server does
+   not wish to receive the message body and is closing the connection, the
+   client <bcp14>SHOULD</bcp14> immediately cease transmitting the body and close its side
+   of the connection.
+</t>
+</section>
+
+<section title="Tear-down" anchor="persistent.tear-down">
+  <iref primary="false" item="Connection header field" x:for-anchor=""/>
+  <iref primary="true" item="close" x:for-anchor=""/>
+  <x:anchor-alias value="close"/>
+<t>
+   The "close" connection option is defined as a signal that the sender
+   will close this connection after completion of the response.
+   A sender <bcp14>SHOULD</bcp14> send a <x:ref>Connection</x:ref> header field
+   (<xref target="HTTP" x:rel="#field.connection"/>) containing the "close" connection option
+   when it intends to close a connection. For example,
+</t>
+<sourcecode type="http-message">
+Connection: close
+</sourcecode>
+<t>
+   as a request header field indicates that this is the last request that
+   the client will send on this connection, while in a response, the same
+   field indicates that the server is going to close this connection after
+   the response message is complete.
+</t>
+<t anchor="field.close">
+   <iref primary="true" item="Fields" subitem="Close" x:for-anchor=""/>
+   <rdf:Description>
+     <comments xmlns="urn:ietf:id:draft-ietf-httpbis-p2-semantics#">(reserved)</comments>
+   </rdf:Description>
+   Note that the field name "Close" is reserved, since using that name as a
+   header field might conflict with the "close" connection option.
+</t>
+<t>
+   A client that sends a "close" connection option <bcp14>MUST NOT</bcp14>
+   send further requests on that connection (after the one containing the
+   "close") and <bcp14>MUST</bcp14> close the connection after reading the
+   final response message corresponding to this request.
+</t>
+<t>
+   A server that receives a "close" connection option <bcp14>MUST</bcp14>
+   initiate closure of the connection (see below) after it sends the
+   final response to the request that contained the "close" connection option.
+   The server <bcp14>SHOULD</bcp14> send a "close" connection option in its final response
+   on that connection. The server <bcp14>MUST NOT</bcp14> process any further requests
+   received on that connection.
+</t>
+<t>
+   A server that sends a "close" connection option <bcp14>MUST</bcp14>
+   initiate closure of the connection (see below) after it sends the
+   response containing the "close" connection option. The server <bcp14>MUST NOT</bcp14> process
+   any further requests received on that connection.
+</t>
+<t>
+   A client that receives a "close" connection option <bcp14>MUST</bcp14>
+   cease sending requests on that connection and close the connection
+   after reading the response message containing the "close" connection option;
+   if additional pipelined requests had been sent on the connection,
+   the client <bcp14>SHOULD NOT</bcp14> assume that they will be processed by the server.
+</t>
+<t>
+   If a server performs an immediate close of a TCP connection, there is a
+   significant risk that the client will not be able to read the last HTTP
+   response.  If the server receives additional data from the client on a
+   fully closed connection, such as another request sent by the
+   client before receiving the server's response, the server's TCP stack will
+   send a reset packet to the client; unfortunately, the reset packet might
+   erase the client's unacknowledged input buffers before they can be read
+   and interpreted by the client's HTTP parser.
+</t>
+<t>
+   To avoid the TCP reset problem, servers typically close a connection in
+   stages. First, the server performs a half-close by closing only the write
+   side of the read/write connection. The server then continues to read from
+   the connection until it receives a corresponding close by the client, or
+   until the server is reasonably certain that its own TCP stack has received
+   the client's acknowledgement of the packet(s) containing the server's last
+   response. Finally, the server fully closes the connection.
+</t>
+<t>
+   It is unknown whether the reset problem is exclusive to TCP or might also
+   be found in other transport connection protocols.
+</t>
+<t>
+   Note that a TCP connection that is half-closed by the client does not
+   delimit a request message, nor does it imply that the client is no longer
+   interested in a response. In general, transport signals cannot be relied
+   upon to signal edge cases, since HTTP/1.1 is independent of transport.
+</t>
+</section>
+
+<section title="TLS Connection Initiation" anchor="tls.connection.initiation">
+<t>
+   Conceptually, HTTP/TLS is simply sending HTTP messages over a connection
+   secured via TLS <xref target="TLS13"/>.
+</t>
+<t>
+   The HTTP client also acts as the TLS client. It initiates a connection to
+   the server on the appropriate port and sends the TLS ClientHello to begin
+   the TLS handshake. When the TLS handshake has finished, the client may then
+   initiate the first HTTP request. All HTTP data <bcp14>MUST</bcp14> be sent as TLS
+   "application data" but is otherwise treated like a normal connection for
+   HTTP (including potential reuse as a persistent connection).
+</t>
+</section>
+
+<section title="TLS Connection Closure" anchor="tls.connection.closure">
+<t>
+   TLS uses an exchange of closure alerts prior to (non-error) connection
+   closure to provide secure connection closure; see <xref section="6.1" target="TLS13"/>.  When a
+   valid closure alert is received, an implementation can be assured that no
+   further data will be received on that connection.
+</t>
+<t>
+   When an implementation knows that it has sent or received all the
+   message data that it cares about, typically by detecting HTTP message
+   boundaries, it might generate an "incomplete close" by sending a
+   closure alert and then closing the connection without waiting to
+   receive the corresponding closure alert from its peer.
+</t>
+<t>
+   An incomplete close does not call into question the security of the data
+   already received, but it could indicate that subsequent data might have been
+   truncated. As TLS is not directly aware of HTTP message framing, it is
+   necessary to examine the HTTP data itself to determine whether messages are
+   complete. Handling of incomplete messages is defined in
+   <xref target="incomplete.messages"/>.
+</t>
+<t>
+   When encountering an incomplete close, a client <bcp14>SHOULD</bcp14> treat as completed
+   all requests for which it has received either
+</t>
+<ol>
+  <li>
+    as much data as specified in the <x:ref>Content-Length</x:ref> header
+    field or
+  </li>
+  <li>
+    the terminal zero-length chunk (when <x:ref>Transfer-Encoding</x:ref> of chunked is used).
+  </li>
+</ol>   
+<t>
+   A response that has neither chunked
+   transfer coding nor Content-Length is complete only if a valid closure alert
+   has been received. Treating an incomplete message as complete could expose
+   implementations to attack.
+</t>
+<t>
+   A client detecting an incomplete close <bcp14>SHOULD</bcp14> recover gracefully.
+</t>
+<t>
+   Clients <bcp14>MUST</bcp14> send a closure alert before closing the connection.
+   Clients that do not expect to receive any more data <bcp14>MAY</bcp14> choose not
+   to wait for the server's closure alert and simply close the
+   connection, thus generating an incomplete close on the server side.
+</t>
+<t>
+   Servers <bcp14>SHOULD</bcp14> be prepared to receive an incomplete close from the client,
+   since the client can often locate the end of server data.
+</t>
+<t>
+   Servers <bcp14>MUST</bcp14> attempt to initiate an exchange of closure alerts with
+   the client before closing the connection. Servers <bcp14>MAY</bcp14> close the
+   connection after sending the closure alert, thus generating an
+   incomplete close on the client side.
+</t>
+</section>
+</section>
+
+<section title="Enclosing Messages as Data" anchor="enclosing.messages">
+
+<section title="Media Type message/http" anchor="media.type.message.http">
+<iref item="Media Type" subitem="message/http" primary="true"/>
+<iref item="message/http Media Type" primary="true"/>
+<t>
+   The "message/http" media type can be used to enclose a single HTTP request or
+   response message, provided that it obeys the MIME restrictions for all
+   "message" types regarding line length and encodings. Because of the line
+   length limitations, field values within "message/http" are allowed to use
+   line folding (<x:ref>obs-fold</x:ref>), as described in
+   <xref target="line.folding"/>, to convey the field value over multiple
+   lines. A recipient of "message/http" data <bcp14>MUST</bcp14> replace any obsolete line
+   folding with one or more SP characters when the message is consumed.
+</t>
+<dl>
+  <dt>Type name:</dt>
+  <dd>message</dd>
+  <dt>Subtype name:</dt>
+  <dd>http</dd>
+  <dt>Required parameters:</dt>
+  <dd>N/A</dd>
+  <dt>Optional parameters:</dt>
+  <dd>
+    <t>version, msgtype</t>
+    <dl>
+      <dt>version:</dt>
+      <dd>
+        The HTTP-version number of the enclosed message
+        (e.g., "1.1"). If not present, the version can be
+        determined from the first line of the body.
+      </dd>
+      <dt>msgtype:</dt>
+      <dd>
+        The message type — "request" or "response". If not
+        present, the type can be determined from the first
+        line of the body.
+      </dd>
+    </dl>
+  </dd>
+  <dt>Encoding considerations:</dt>
+  <dd>only "7bit", "8bit", or "binary" are permitted</dd>
+  <dt>Security considerations:</dt>
+  <dd>see <xref target="security.considerations"/></dd>
+  <dt>Interoperability considerations:</dt>
+  <dd>N/A</dd>
+  <dt>Published specification:</dt>
+  <dd>RFC 9112 (see <xref target="media.type.message.http"/>).</dd>
+  <dt>Applications that use this media type:</dt>
+  <dd>N/A</dd>
+  <dt>Fragment identifier considerations:</dt>
+  <dd>N/A</dd>
+  <dt>Additional information:</dt>
+  <dd>
+    <dl>
+      <dt>Magic number(s):</dt>
+      <dd>N/A</dd>
+      <dt>Deprecated alias names for this type:</dt>
+      <dd>N/A</dd>
+      <dt>File extension(s):</dt>
+      <dd>N/A</dd>
+      <dt>Macintosh file type code(s):</dt>
+      <dd>N/A</dd>
+    </dl>
+  </dd>
+  <dt>Person and email address to contact for further information:</dt>
+  <dd>See Authors' Addresses section.</dd>
+  <dt>Intended usage:</dt>
+  <dd>COMMON</dd>
+  <dt>Restrictions on usage:</dt>
+  <dd>N/A</dd>
+  <dt>Author:</dt>
+  <dd>See Authors' Addresses section.</dd>
+  <dt>Change controller:</dt>
+  <dd>IESG</dd>
+</dl>
+</section>
+
+<section title="Media Type application/http" anchor="media.type.application.http">
+<iref item="Media Type" subitem="application/http" primary="true"/>
+<iref item="application/http Media Type" primary="true"/>
+<t>
+   The "application/http" media type can be used to enclose a pipeline of one or more
+   HTTP request or response messages (not intermixed).
+</t>
+<dl>
+  <dt>Type name:</dt>
+  <dd>application</dd>
+  <dt>Subtype name:</dt>
+  <dd>http</dd>
+  <dt>Required parameters:</dt>
+  <dd>N/A</dd>
+  <dt>Optional parameters:</dt>
+  <dd>
+    <t>
+      version, msgtype
+    </t>
+    <dl>
+      <dt>version:</dt>
+      <dd>
+        The HTTP-version number of the enclosed messages
+        (e.g., "1.1"). If not present, the version can be
+        determined from the first line of the body.
+      </dd>
+      <dt>msgtype:</dt>
+      <dd>
+        The message type — "request" or "response". If not
+        present, the type can be determined from the first
+        line of the body.
+      </dd>
+    </dl>
+  </dd>
+  <dt>Encoding considerations:</dt>
+  <dd>
+    HTTP messages enclosed by this type
+    are in "binary" format; use of an appropriate
+    Content-Transfer-Encoding is required when
+    transmitted via email.
+  </dd>
+  <dt>Security considerations:</dt>
+  <dd>
+    see <xref target="security.considerations"/>
+  </dd>
+  <dt>Interoperability considerations:</dt>
+  <dd>N/A</dd>
+  <dt>Published specification:</dt>
+  <dd>
+    RFC 9112 (see <xref target="media.type.application.http"/>).
+  </dd>
+  <dt>Applications that use this media type:</dt>
+  <dd>N/A</dd>
+  <dt>Fragment identifier considerations:</dt>
+  <dd>N/A</dd>
+  <dt>Additional information:</dt>
+  <dd>
+    <dl>
+      <dt>Deprecated alias names for this type:</dt>
+      <dd>N/A</dd>
+      <dt>Magic number(s):</dt>
+      <dd>N/A</dd>
+      <dt>File extension(s):</dt>
+      <dd>N/A</dd>
+      <dt>Macintosh file type code(s):</dt>
+      <dd>N/A</dd>
+    </dl>
+  </dd>
+  <dt>Person and email address to contact for further information:</dt>
+  <dd>See Authors' Addresses section.</dd>
+  <dt>Intended usage:</dt>
+  <dd>COMMON</dd>
+  <dt>Restrictions on usage:</dt>
+  <dd>N/A</dd>
+  <dt>Author:</dt>
+  <dd>See Authors' Addresses section.</dd>
+  <dt>Change controller:</dt>
+  <dd>IESG</dd>
+</dl>
+</section>
+</section>
+
+<section title="Security Considerations" anchor="security.considerations">
+<t>
+   This section is meant to inform developers, information providers, and
+   users about known security considerations relevant to HTTP message syntax
+   and parsing. Security considerations about HTTP semantics,
+   content, and routing are addressed in <xref target="HTTP"/>.
+</t>
+
+<section title="Response Splitting" anchor="response.splitting">
+<t>
+   Response splitting (a.k.a. CRLF injection) is a common technique, used in
+   various attacks on Web usage, that exploits the line-based nature of HTTP
+   message framing and the ordered association of requests to responses on
+   persistent connections <xref target="Klein"/>. This technique can be
+   particularly damaging when the requests pass through a shared cache.
+</t>
+<t>
+   Response splitting exploits a vulnerability in servers (usually within an
+   application server) where an attacker can send encoded data within some
+   parameter of the request that is later decoded and echoed within any of the
+   response header fields of the response. If the decoded data is crafted to
+   look like the response has ended and a subsequent response has begun, the
+   response has been split, and the content within the apparent second response
+   is controlled by the attacker. The attacker can then make any other request
+   on the same persistent connection and trick the recipients (including
+   intermediaries) into believing that the second half of the split is an
+   authoritative answer to the second request.
+</t>
+<t>
+   For example, a parameter within the request-target might be read by an
+   application server and reused within a redirect, resulting in the same
+   parameter being echoed in the <x:ref>Location</x:ref> header field of the
+   response. If the parameter is decoded by the application and not properly
+   encoded when placed in the response field, the attacker can send encoded
+   CRLF octets and other content that will make the application's single
+   response look like two or more responses.
+</t>
+<t>
+   A common defense against response splitting is to filter requests for data
+   that looks like encoded CR and LF (e.g., "%0D" and "%0A"). However, that
+   assumes the application server is only performing URI decoding rather
+   than more obscure data transformations like charset transcoding, XML entity
+   translation, base64 decoding, sprintf reformatting, etc.  A more effective
+   mitigation is to prevent anything other than the server's core protocol
+   libraries from sending a CR or LF within the header section, which means
+   restricting the output of header fields to APIs that filter for bad octets
+   and not allowing application servers to write directly to the protocol
+   stream.
+</t>
+</section>
+
+<section title="Request Smuggling" anchor="request.smuggling">
+<t>
+   Request smuggling (<xref target="Linhart"/>) is a technique that exploits
+   differences in protocol parsing among various recipients to hide additional
+   requests (which might otherwise be blocked or disabled by policy) within an
+   apparently harmless request.  Like response splitting, request smuggling
+   can lead to a variety of attacks on HTTP usage.
+</t>
+<t>
+   This specification has introduced new requirements on request parsing,
+   particularly with regard to message framing in
+   <xref target="message.body.length"/>, to reduce the effectiveness of
+   request smuggling.
+</t>
+</section>
+
+<section title="Message Integrity" anchor="message.integrity">
+<t>
+   HTTP does not define a specific mechanism for ensuring message integrity,
+   instead relying on the error-detection ability of underlying transport
+   protocols and the use of length or chunk-delimited framing to detect
+   completeness. Historically, the lack of
+   a single integrity mechanism has been justified by the informal nature of
+   most HTTP communication.  However, the prevalence of HTTP as an information
+   access mechanism has resulted in its increasing use within environments
+   where verification of message integrity is crucial.
+</t>
+<t>
+   The mechanisms provided with the "https" scheme, such as authenticated
+   encryption, provide protection against modification of messages. Care
+   is needed, however, to ensure that connection closure cannot be used to
+   truncate messages (see <xref target="tls.connection.closure"/>). User agents
+   might refuse to accept incomplete messages or treat them specially. For
+   example, a browser being used to view medical history or drug interaction
+   information needs to indicate to the user when such information is detected
+   by the protocol to be incomplete, expired, or corrupted during transfer.
+   Such mechanisms might be selectively enabled via user agent extensions or
+   the presence of message integrity metadata in a response.
+</t>
+<t>
+   The "http" scheme provides no protection against accidental or malicious
+   modification of messages.
+</t>
+<t>
+   Extensions to the protocol might be used to mitigate the risk of unwanted
+   modification of messages by intermediaries, even when the "https" scheme is
+   used. Integrity might be assured by using message authentication codes
+   or digital
+   signatures that are selectively added to messages via extensible metadata
+   fields.
+</t>
+</section>
+
+<section title="Message Confidentiality" anchor="message.confidentiality">
+<t>
+   HTTP relies on underlying transport protocols to provide message
+   confidentiality when that is desired. HTTP has been specifically designed
+   to be independent of the transport protocol, such that it can be used
+   over many forms of encrypted connection, with the selection of
+   such transports being identified by the choice of URI scheme or within
+   user agent configuration.
+</t>
+<t>
+   The "https" scheme can be used to identify resources that require a
+   confidential connection, as described in <xref target="HTTP" x:rel="#https.uri"/>.
+</t>
+</section>
+</section>
+
+<section title="IANA Considerations" anchor="IANA.considerations">
+<t>
+   The change controller for the following registrations is:
+   "IETF (iesg@ietf.org) - Internet Engineering Task Force".
+</t>
+
+<section title="Field Name Registration" anchor="field.name.registration">
+<t>
+   IANA has added the following field names to the "Hypertext Transfer Protocol (HTTP) Field
+   Name Registry" at <eref target="https://www.iana.org/assignments/http-fields"/>,
+   as described in <xref target="HTTP" x:rel="#field.name.registration"/>.
+</t>
+<?BEGININC build/draft-ietf-httpbis-messaging-latest.iana-headers ?>
+<!--AUTOGENERATED FROM extract-header-defs.xslt, do not edit manually-->
+<table align="left" anchor="iana.header.registration.table">
+   <thead>
+      <tr>
+         <th>Field Name</th>
+         <th>Status</th>
+         <th>Section</th>
+         <th>Comments</th>
+      </tr>
+   </thead>
+   <tbody>
+      <tr>
+         <td>Close</td>
+         <td>permanent</td>
+         <td>
+            <xref target="persistent.tear-down" format="counter"/>
+         </td>
+         <td>(reserved)</td>
+      </tr>
+      <tr>
+         <td>MIME-Version</td>
+         <td>permanent</td>
+         <td>
+            <xref target="mime-version" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+      <tr>
+         <td>Transfer-Encoding</td>
+         <td>permanent</td>
+         <td>
+            <xref target="field.transfer-encoding" format="counter"/>
+         </td>
+         <td/>
+      </tr>
+   </tbody>
+</table>
+<!--(END)-->
+<?ENDINC build/draft-ietf-httpbis-messaging-latest.iana-headers ?>
+</section>
+
+<section title="Media Type Registration" anchor="media.type.http">
+<t>
+   IANA has updated the "Media Types" registry at
+   <eref target="https://www.iana.org/assignments/media-types"/>
+   with the registration information in Sections
+   <xref target="media.type.message.http" format="counter"/> and
+   <xref target="media.type.application.http" format="counter"/> for the media types
+   "message/http" and "application/http", respectively.
+</t>
+</section>
+
+<section title="Transfer Coding Registration" anchor="transfer.coding.registration">
+<t>
+   IANA has updated the "HTTP Transfer Coding Registry" at
+   <eref target="https://www.iana.org/assignments/http-parameters/"/>
+   with the registration procedure of <xref target="transfer.coding.registry"/>
+   and the content coding names summarized in the table below.
+</t>
+<table align="left" anchor="iana.transfer.coding.registration.table">
+  <thead>
+    <tr>
+      <th>Name</th>
+      <th>Description</th>
+      <th>Section</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>chunked</td>
+      <td>Transfer in a series of chunks</td>
+      <td><xref target="chunked.encoding" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>compress</td>
+      <td>UNIX "compress" data format <xref target="Welch"/></td>
+      <td><xref target="compression.codings" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>deflate</td>
+      <td>"deflate" compressed data (<xref target="RFC1951"/>) inside
+      the "zlib" data format (<xref target="RFC1950"/>)</td>
+      <td><xref target="compression.codings" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>gzip</td>
+      <td>GZIP file format <xref target="RFC1952"/></td>
+      <td><xref target="compression.codings" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>trailers</td>
+      <td>(reserved)</td>
+      <td><xref target="transfer.coding.registration" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>x-compress</td>
+      <td>Deprecated (alias for compress)</td>
+      <td><xref target="compression.codings" format="counter"/></td>
+    </tr>
+    <tr>
+      <td>x-gzip</td>
+      <td>Deprecated (alias for gzip)</td>
+      <td><xref target="compression.codings" format="counter"/></td>
+    </tr>
+  </tbody>
+</table>
+<aside>
+  <t>
+    <x:h>Note:</x:h> the coding name "trailers" is reserved because its use would
+    conflict with the keyword "trailers" in the <x:ref>TE</x:ref>
+    header field (<xref target="HTTP" x:rel="#field.te"/>).
+  </t>
+</aside>
+</section>
+
+<section title="ALPN Protocol ID Registration" anchor="alpn.registration">
+<t>
+   IANA has updated the
+   "TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs" registry at
+   <eref target="https://www.iana.org/assignments/tls-extensiontype-values/"/>
+   with the registration below:
+</t>
+<table>
+  <thead>
+    <tr>
+      <th>Protocol</th>
+      <th>Identification Sequence</th>
+      <th>Reference</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>HTTP/1.1</td>
+      <td>0x68 0x74 0x74 0x70 0x2f 0x31 0x2e 0x31 ("http/1.1")</td>
+      <td>RFC 9112</td>
+    </tr>
+  </tbody>
+</table>
+</section>
+
+</section>
+
+</middle>
+<back>
+<displayreference target="HTTP10" to="HTTP/1.0"/>
+
+<references title="Normative References">
+
+<reference anchor="HTTP">
+  <x:source href="rfc9110.xml" basename="rfc9110">
+    <x:defines>OWS</x:defines>
+    <x:defines>RWS</x:defines>
+    <x:defines>BWS</x:defines>
+    <x:defines>port</x:defines>
+    <x:defines>transfer-coding</x:defines>
+    <x:defines>uri-host</x:defines>
+  </x:source>
+</reference>
+
+<reference anchor="CACHING">
+  <x:source href="rfc9111.xml" basename="rfc9111"/>
+</reference>
+
+<reference anchor="URI">
+ <front>
+  <title abbrev="URI Generic Syntax">Uniform Resource Identifier (URI): Generic Syntax</title>
+  <author initials="T." surname="Berners-Lee" fullname="Tim Berners-Lee"/>
+  <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+  <author initials="L." surname="Masinter" fullname="Larry Masinter"/>
+  <date month="January" year="2005"/>
+ </front>
+ <seriesInfo name="STD" value="66"/>
+ <seriesInfo name="RFC" value="3986"/>
+</reference>
+
+<reference anchor="RFC5234">
+  <front>
+    <title abbrev="ABNF for Syntax Specifications">Augmented BNF for Syntax Specifications: ABNF</title>
+    <author initials="D." surname="Crocker" fullname="Dave Crocker" role="editor"/>
+    <author initials="P." surname="Overell" fullname="Paul Overell"/>
+    <date month="January" year="2008"/>
+  </front>
+  <seriesInfo name="STD" value="68"/>
+  <seriesInfo name="RFC" value="5234"/>
+</reference>
+
+<reference anchor="RFC7405">
+  <front>
+    <title>Case-Sensitive String Support in ABNF</title>
+    <author initials="P." surname="Kyzivat" fullname="Dave Kyzivat"/>
+    <date month="December" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7405"/>
+</reference>
+
+<reference anchor="RFC2119">
+  <front>
+    <title>Key words for use in RFCs to Indicate Requirement Levels</title>
+    <author initials="S." surname="Bradner" fullname="Scott Bradner"/>
+    <date month="March" year="1997"/>
+  </front>
+  <seriesInfo name="BCP" value="14"/>
+  <seriesInfo name="RFC" value="2119"/>
+</reference>
+
+<reference anchor="RFC8174">
+  <front>
+    <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
+    <author initials="B." surname="Leiba" fullname="Barry Leiba"/>
+    <date year="2017" month="May"/>
+  </front>
+  <seriesInfo name="BCP" value="14"/>
+  <seriesInfo name="RFC" value="8174"/>
+</reference>
+
+<reference anchor="USASCII">
+  <front>
+    <title>Coded Character Set -- 7-bit American Standard Code for Information Interchange</title>
+    <author>
+      <organization>American National Standards Institute</organization>
+    </author>
+    <date year="1986"/>
+  </front>
+  <seriesInfo name="ANSI" value="X3.4"/>
+</reference>
+
+<reference anchor="RFC1950">
+  <front>
+    <title>ZLIB Compressed Data Format Specification version 3.3</title>
+    <author initials="P." surname="Deutsch" fullname="L. Peter Deutsch"/>
+    <author initials="J-L." surname="Gailly" fullname="Jean-Loup Gailly"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1950"/>
+</reference>
+
+<reference anchor="RFC1951">
+  <front>
+    <title>DEFLATE Compressed Data Format Specification version 1.3</title>
+    <author initials="P." surname="Deutsch" fullname="L. Peter Deutsch"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1951"/>
+</reference>
+
+<reference anchor="RFC1952">
+  <front>
+    <title>GZIP file format specification version 4.3</title>
+    <author initials="P." surname="Deutsch" fullname="L. Peter Deutsch"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1952"/>
+</reference>
+
+<reference anchor="TLS13">
+   <front>
+      <title>The Transport Layer Security (TLS) Protocol Version 1.3</title>
+      <author initials="E." surname="Rescorla" fullname="E. Rescorla"/>
+      <date year="2018" month="August"/>
+   </front>
+   <seriesInfo name="RFC" value="8446"/>
+</reference>
+
+<reference anchor="Welch" target="https://ieeexplore.ieee.org/document/1659158/">
+  <front>
+    <title>A Technique for High-Performance Data Compression</title>
+    <author initials="T." surname="Welch" fullname="Terry A. Welch"/>
+    <date month="June" year="1984"/>
+  </front>
+  <seriesInfo name="IEEE Computer" value="17(6)"/>
+  <seriesInfo name="DOI" value="10.1109/MC.1984.1659158"/>
+</reference>
+
+</references>
+
+<references title="Informative References" anchor="informative.references">
+
+<reference anchor="HTTP10">
+  <front>
+    <title abbrev="HTTP/1.0">Hypertext Transfer Protocol -- HTTP/1.0</title>
+    <author initials="T." surname="Berners-Lee" fullname="Tim Berners-Lee"/>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+    <author initials="H." surname="Frystyk" fullname="Henrik Frystyk Nielsen"/>
+    <date month="May" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="1945"/>
+</reference>
+
+<reference anchor="RFC2045">
+  <front>
+    <title abbrev="Internet Message Bodies">Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</title>
+    <author initials="N." surname="Freed" fullname="Ned Freed"/>
+    <author initials="N." surname="Borenstein" fullname="Nathaniel S. Borenstein"/>
+    <date month="November" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="2045"/>
+</reference>
+
+<reference anchor="RFC2046">
+  <front>
+    <title abbrev="Media Types">Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types</title>
+    <author initials="N." surname="Freed" fullname="Ned Freed"/>
+    <author initials="N." surname="Borenstein" fullname="Nathaniel S. Borenstein"/>
+    <date month="November" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="2046"/>
+</reference>
+
+<reference anchor="RFC2049">
+  <front>
+    <title abbrev="MIME Conformance">Multipurpose Internet Mail Extensions (MIME) Part Five: Conformance Criteria and Examples</title>
+    <author initials="N." surname="Freed" fullname="Ned Freed"/>
+    <author initials="N." surname="Borenstein" fullname="Nathaniel S. Borenstein"/>
+    <date month="November" year="1996"/>
+  </front>
+  <seriesInfo name="RFC" value="2049"/>
+</reference>
+
+<reference anchor="RFC2068">
+  <front>
+    <title>Hypertext Transfer Protocol -- HTTP/1.1</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding"/>
+    <author initials="J." surname="Gettys" fullname="Jim Gettys"/>
+    <author initials="J." surname="Mogul" fullname="Jeffrey C. Mogul"/>
+    <author initials="H." surname="Frystyk" fullname="Henrik Frystyk Nielsen"/>
+    <author initials="T." surname="Berners-Lee" fullname="Tim Berners-Lee"/>
+    <date month="January" year="1997"/>
+  </front>
+  <seriesInfo name="RFC" value="2068"/>
+</reference>
+
+<reference anchor="RFC2557">
+  <front>
+    <title abbrev="MIME Encapsulation of Aggregate Documents">MIME Encapsulation of Aggregate Documents, such as HTML (MHTML)</title>
+    <author initials="J." surname="Palme" fullname="Jacob Palme"/>
+    <author initials="A." surname="Hopmann" fullname="Alex Hopmann"/>
+    <author initials="N." surname="Shelness" fullname="Nick Shelness"/>
+    <date year="1999" month="March"/>
+  </front>
+  <seriesInfo name="RFC" value="2557"/>
+</reference>
+
+<reference anchor="RFC5322">
+  <front>
+    <title>Internet Message Format</title>
+    <author initials="P." surname="Resnick" fullname="P. Resnick" role="editor"/>
+    <date year="2008" month="October"/>
+  </front>
+  <seriesInfo name="RFC" value="5322"/>
+</reference>
+
+<reference anchor="RFC7230">
+  <front>
+    <title>Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing</title>
+    <author initials="R." surname="Fielding" fullname="Roy T. Fielding" role="editor"/>
+    <author initials="J." surname="Reschke" fullname="Julian F. Reschke" role="editor"/>
+    <date month="June" year="2014"/>
+  </front>
+  <seriesInfo name="RFC" value="7230"/>
+</reference>
+
+<reference anchor="RFC8126">
+  <front>
+    <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
+    <author initials="M." surname="Cotton" fullname="M. Cotton"/>
+    <author initials="B." surname="Leiba" fullname="B. Leiba"/>
+    <author initials="T." surname="Narten" fullname="T. Narten"/>
+    <date year="2017" month="June"/>
+  </front>
+  <seriesInfo name="BCP" value="26"/>
+  <seriesInfo name="RFC" value="8126"/>
+</reference>
+
+<reference anchor="Klein" target="https://packetstormsecurity.com/papers/general/whitepaper_httpresponse.pdf">
+  <front>
+    <title>Divide and Conquer - HTTP Response Splitting, Web Cache Poisoning Attacks, and Related Topics</title>
+    <author initials="A." surname="Klein" fullname="Amit Klein"/>
+    <date year="2004" month="March"/>
+  </front>
+</reference>
+
+<reference anchor="Linhart" target="https://www.cgisecurity.com/lib/HTTP-Request-Smuggling.pdf">
+  <front>
+    <title>HTTP Request Smuggling</title>
+    <author initials="C." surname="Linhart" fullname="Chaim Linhart"/>
+    <author initials="A." surname="Klein" fullname="Amit Klein"/>
+    <author initials="R." surname="Heled" fullname="Ronen Heled"/>
+    <author initials="S." surname="Orrin" fullname="Steve Orrin"/>
+    <date year="2005" month="June"/>
+  </front>
+</reference>
+
+</references>
+
+<?BEGININC build/draft-ietf-httpbis-messaging-latest.abnf-appendix ?>
+<section title="Collected ABNF" anchor="collected.abnf"><t>In the collected ABNF below, list rules are expanded per <xref target="HTTP" x:rel="#abnf.extension"/>.</t><sourcecode type="abnf" name="rfc9112.parsed-abnf">
+<x:ref>BWS</x:ref> = &lt;BWS, see <xref target="HTTP" x:fmt="," x:sec="5.6.3"/>&gt;
+
+<x:ref>HTTP-message</x:ref> = start-line CRLF *( field-line CRLF ) CRLF [
+ message-body ]
+<x:ref>HTTP-name</x:ref> = %x48.54.54.50 ; HTTP
+<x:ref>HTTP-version</x:ref> = HTTP-name "/" DIGIT "." DIGIT
+
+<x:ref>OWS</x:ref> = &lt;OWS, see <xref target="HTTP" x:fmt="," x:sec="5.6.3"/>&gt;
+
+<x:ref>RWS</x:ref> = &lt;RWS, see <xref target="HTTP" x:fmt="," x:sec="5.6.3"/>&gt;
+
+<x:ref>Transfer-Encoding</x:ref> = [ transfer-coding *( OWS "," OWS transfer-coding
+ ) ]
+
+<x:ref>absolute-URI</x:ref> = &lt;absolute-URI, see <xref target="URI" x:fmt="," x:sec="4.3"/>&gt;
+<x:ref>absolute-form</x:ref> = absolute-URI
+<x:ref>absolute-path</x:ref> = &lt;absolute-path, see <xref target="HTTP" x:fmt="," x:sec="4.1"/>&gt;
+<x:ref>asterisk-form</x:ref> = "*"
+<x:ref>authority</x:ref> = &lt;authority, see <xref target="URI" x:fmt="," x:sec="3.2"/>&gt;
+<x:ref>authority-form</x:ref> = uri-host ":" port
+
+<x:ref>chunk</x:ref> = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
+<x:ref>chunk-data</x:ref> = 1*OCTET
+<x:ref>chunk-ext</x:ref> = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val
+ ] )
+<x:ref>chunk-ext-name</x:ref> = token
+<x:ref>chunk-ext-val</x:ref> = token / quoted-string
+<x:ref>chunk-size</x:ref> = 1*HEXDIG
+<x:ref>chunked-body</x:ref> = *chunk last-chunk trailer-section CRLF
+
+<x:ref>field-line</x:ref> = field-name ":" OWS field-value OWS
+<x:ref>field-name</x:ref> = &lt;field-name, see <xref target="HTTP" x:fmt="," x:sec="5.1"/>&gt;
+<x:ref>field-value</x:ref> = &lt;field-value, see <xref target="HTTP" x:fmt="," x:sec="5.5"/>&gt;
+
+<x:ref>last-chunk</x:ref> = 1*"0" [ chunk-ext ] CRLF
+
+<x:ref>message-body</x:ref> = *OCTET
+<x:ref>method</x:ref> = token
+
+<x:ref>obs-fold</x:ref> = OWS CRLF RWS
+<x:ref>obs-text</x:ref> = &lt;obs-text, see <xref target="HTTP" x:fmt="," x:sec="5.6.4"/>&gt;
+<x:ref>origin-form</x:ref> = absolute-path [ "?" query ]
+
+<x:ref>port</x:ref> = &lt;port, see <xref target="URI" x:fmt="," x:sec="3.2.3"/>&gt;
+
+<x:ref>query</x:ref> = &lt;query, see <xref target="URI" x:fmt="," x:sec="3.4"/>&gt;
+<x:ref>quoted-string</x:ref> = &lt;quoted-string, see <xref target="HTTP" x:fmt="," x:sec="5.6.4"/>&gt;
+
+<x:ref>reason-phrase</x:ref> = 1*( HTAB / SP / VCHAR / obs-text )
+<x:ref>request-line</x:ref> = method SP request-target SP HTTP-version
+<x:ref>request-target</x:ref> = origin-form / absolute-form / authority-form /
+ asterisk-form
+
+<x:ref>start-line</x:ref> = request-line / status-line
+<x:ref>status-code</x:ref> = 3DIGIT
+<x:ref>status-line</x:ref> = HTTP-version SP status-code SP [ reason-phrase ]
+
+<x:ref>token</x:ref> = &lt;token, see <xref target="HTTP" x:fmt="," x:sec="5.6.2"/>&gt;
+<x:ref>trailer-section</x:ref> = *( field-line CRLF )
+<x:ref>transfer-coding</x:ref> = &lt;transfer-coding, see <xref target="HTTP" x:fmt="," x:sec="10.1.4"/>&gt;
+
+<x:ref>uri-host</x:ref> = &lt;host, see <xref target="URI" x:fmt="," x:sec="3.2.2"/>&gt;
+</sourcecode>
+</section>
+<?ENDINC build/draft-ietf-httpbis-messaging-latest.abnf-appendix ?>
+
+<section title="Differences between HTTP and MIME" anchor="differences.between.http.and.mime">
+<t>
+   HTTP/1.1 uses many of the constructs defined for the Internet Message
+   Format <xref target="RFC5322"/> and Multipurpose Internet Mail Extensions (MIME)
+   <xref target="RFC2045"/> to allow a message body to be transmitted in an open
+   variety of representations and with extensible fields.  However, some
+   of these constructs have been reinterpreted to better fit the needs
+   of interactive communication, leading to some differences in how MIME
+   constructs are used within HTTP. These differences were carefully
+   chosen to optimize performance over binary connections, allow
+   greater freedom in the use of new media types, ease date comparisons,
+   and accommodate common implementations.
+</t>
+<t>
+   This appendix describes specific areas where HTTP differs from MIME.
+   Proxies and gateways to and from strict MIME environments need to be
+   aware of these differences and provide the appropriate conversions
+   where necessary.
+</t>
+
+<section title="MIME-Version" anchor="mime-version">
+  <iref primary="true" item="Fields" subitem="MIME-Version" x:for-anchor=""/><iref primary="true" item="Header Fields" subitem="MIME-Version" x:for-anchor=""/><iref primary="true" item="MIME-Version header field" x:for-anchor=""/>
+  <x:anchor-alias value="MIME-Version"/>
+<t>
+   HTTP is not a MIME-compliant protocol. However, messages can
+   include a single MIME-Version header field to indicate what
+   version of the MIME protocol was used to construct the message. Use
+   of the MIME-Version header field indicates that the message is in
+   full conformance with the MIME protocol (as defined in <xref target="RFC2045"/>).
+   Senders are responsible for ensuring full conformance (where
+   possible) when exporting HTTP messages to strict MIME environments.
+</t>
+</section>
+
+<section title="Conversion to Canonical Form" anchor="conversion.to.canonical.form">
+<t>
+   MIME requires that an Internet mail body part be converted to canonical
+   form prior to being transferred, as described in <xref target="RFC2049" x:fmt="of" x:sec="4"/>, and that content with a type of "text" represents
+   line breaks as CRLF, forbidding the use of CR or LF outside of line break
+   sequences <xref target="RFC2046"/>. In contrast, HTTP does not care whether
+   CRLF, bare CR, or bare LF are used to indicate a line break within content.
+</t>
+<t>
+   A proxy or gateway from HTTP to a strict MIME
+   environment ought to translate all line breaks within text media
+   types to the RFC 2049 canonical form of CRLF. Note, however,
+   this might be complicated by the presence of a <x:ref>Content-Encoding</x:ref>
+   and by the fact that HTTP allows the use of some charsets
+   that do not use octets 13 and 10 to represent CR and LF, respectively.
+</t>
+<t>
+   Conversion will break any cryptographic
+   checksums applied to the original content unless the original content
+   is already in canonical form. Therefore, the canonical form is
+   recommended for any content that uses such checksums in HTTP.
+</t>
+</section>
+
+<section title="Conversion of Date Formats" anchor="conversion.of.date.formats">
+<t>
+   HTTP/1.1 uses a restricted set of date formats (<xref target="HTTP" x:rel="#http.date"/>) to
+   simplify the process of date comparison. Proxies and gateways from
+   other protocols ought to ensure that any <x:ref>Date</x:ref> header field
+   present in a message conforms to one of the HTTP/1.1 formats and rewrite
+   the date if necessary.
+</t>
+</section>
+
+<section title="Conversion of Content-Encoding" anchor="conversion.of.content-encoding">
+<t>
+   MIME does not include any concept equivalent to HTTP's
+   <x:ref>Content-Encoding</x:ref> header field. Since this acts as a modifier
+   on the media type, proxies and gateways from HTTP to MIME-compliant
+   protocols ought to either change the value of the <x:ref>Content-Type</x:ref>
+   header field or decode the representation before forwarding the message.
+   (Some experimental applications of Content-Type for Internet mail have used
+   a media-type parameter of ";conversions=&lt;content-coding&gt;" to perform
+   a function equivalent to Content-Encoding. However, this parameter is
+   not part of the MIME standards.)
+</t>
+</section>
+
+<section title="Conversion of Content-Transfer-Encoding" anchor="conversion.of.content-transfer-encoding">
+  <iref item="Content-Transfer-Encoding header field" x:for-anchor=""/>
+<t>
+   HTTP does not use the Content-Transfer-Encoding field of MIME.
+   Proxies and gateways from MIME-compliant protocols to HTTP need to remove
+   any Content-Transfer-Encoding prior to delivering the response message to
+   an HTTP client.
+</t>
+<t>
+   Proxies and gateways from HTTP to MIME-compliant protocols are
+   responsible for ensuring that the message is in the correct format
+   and encoding for safe transport on that protocol, where "safe
+   transport" is defined by the limitations of the protocol being used.
+   Such a proxy or gateway ought to transform and label the data with an
+   appropriate Content-Transfer-Encoding if doing so will improve the
+   likelihood of safe transport over the destination protocol.
+</t>
+</section>
+
+<section title="MHTML and Line Length Limitations" anchor="mhtml.line.length">
+<t>
+   HTTP implementations that share code with MHTML <xref target="RFC2557"/>
+   implementations need to be aware of MIME line length limitations.
+   Since HTTP does not have this limitation, HTTP does not fold long lines.
+   MHTML messages being transported by HTTP follow all conventions of MHTML,
+   including line length limitations and folding, canonicalization, etc.,
+   since HTTP transfers message-bodies without modification and, aside from the
+   "multipart/byteranges" type (<xref target="HTTP" x:rel="#multipart.byteranges"/>),
+   does not interpret
+   the content or any MIME header lines that might be contained therein.
+</t>
+</section>
+</section>
+
+<section title="Changes from Previous RFCs" anchor="changes">
+<section title="Changes from HTTP/0.9" anchor="changes.from.0.9">
+<t>
+   Since HTTP/0.9 did not support header fields in a request, there is no
+   mechanism for it to support name-based virtual hosts (selection of resource
+   by inspection of the <x:ref>Host</x:ref> header field).
+   Any server that implements name-based virtual hosts ought to disable
+   support for HTTP/0.9. Most requests that appear to be HTTP/0.9 are, in
+   fact, badly constructed HTTP/1.x requests caused by a client failing to
+   properly encode the request-target.
+</t>
+</section>
+
+<section title="Changes from HTTP/1.0" anchor="changes.from.1.0">
+<section title="Multihomed Web Servers" anchor="changes.to.simplify.multihomed.web.servers.and.conserve.ip.addresses">
+<t>
+   The requirements that clients and servers support the <x:ref>Host</x:ref>
+   header field (<xref target="HTTP" x:rel="#field.host"/>), report an error if it is
+   missing from an HTTP/1.1 request, and accept absolute URIs
+   (<xref target="request.target"/>)
+   are among the most important changes defined by HTTP/1.1.
+</t>
+<t>
+   Older HTTP/1.0 clients assumed a one-to-one relationship of IP
+   addresses and servers; there was no established mechanism for
+   distinguishing the intended server of a request other than the IP address
+   to which that request was directed. The <x:ref>Host</x:ref> header field was
+   introduced during the development of HTTP/1.1 and, though it was
+   quickly implemented by most HTTP/1.0 browsers, additional requirements
+   were placed on all HTTP/1.1 requests in order to ensure complete
+   adoption.  At the time of this writing, most HTTP-based services
+   are dependent upon the Host header field for targeting requests.
+</t>
+</section>
+
+<section title="Keep-Alive Connections" anchor="compatibility.with.http.1.0.persistent.connections">
+<t>
+   In HTTP/1.0, each connection is established by the client prior to the
+   request and closed by the server after sending the response. However, some
+   implementations implement the explicitly negotiated ("Keep-Alive") version
+   of persistent connections described in <xref x:sec="19.7.1" x:fmt="of" target="RFC2068"/>.
+</t>
+<t>
+   Some clients and servers might wish to be compatible with these previous
+   approaches to persistent connections, by explicitly negotiating for them
+   with a "Connection: keep-alive" request header field. However, some
+   experimental implementations of HTTP/1.0 persistent connections are faulty;
+   for example, if an HTTP/1.0 proxy server doesn't understand
+   <x:ref>Connection</x:ref>, it will erroneously forward that header field
+   to the next inbound server, which would result in a hung connection.
+</t>
+<t>
+   One attempted solution was the introduction of a Proxy-Connection header
+   field, targeted specifically at proxies. In practice, this was also
+   unworkable, because proxies are often deployed in multiple layers, bringing
+   about the same problem discussed above.
+</t>
+<t>
+   As a result, clients are encouraged not to send the Proxy-Connection header
+   field in any requests.
+</t>
+<t>
+   Clients are also encouraged to consider the use of "Connection: keep-alive"
+   in requests carefully; while they can enable persistent connections with
+   HTTP/1.0 servers, clients using them will need to monitor the
+   connection for "hung" requests (which indicate that the client ought to stop
+   sending the header field), and this mechanism ought not be used by clients
+   at all when a proxy is being used.
+</t>
+</section>
+
+<section title="Introduction of Transfer-Encoding" anchor="introduction.of.transfer-encoding">
+<t>
+   HTTP/1.1 introduces the <x:ref>Transfer-Encoding</x:ref> header field
+   (<xref target="field.transfer-encoding"/>).
+   Transfer codings need to be decoded prior to forwarding an HTTP message
+   over a MIME-compliant protocol.
+</t>
+</section>
+</section>
+
+<section title="Changes from RFC 7230" anchor="changes.from.rfc.7230">
+<t>
+   Most of the sections introducing HTTP's design goals, history, architecture,
+   conformance criteria, protocol versioning, URIs, message routing, and
+   header fields have been moved to <xref target="HTTP"/>.
+   This document has been reduced to just the messaging syntax and
+   connection management requirements specific to HTTP/1.1.
+</t>
+<t>
+   Bare CRs have been prohibited outside of content.
+   (<xref target="message.parsing"/>)
+</t>
+<t>
+   The ABNF definition of <x:ref>authority-form</x:ref> has changed from the
+   more general authority component of a URI (in which port is optional) to
+   the specific host:port format that is required by CONNECT.
+   (<xref target="authority-form"/>)
+</t>
+<t>
+   Recipients are required to avoid smuggling/splitting attacks when processing an
+   ambiguous message framing.
+   (<xref target="field.transfer-encoding"/>)
+</t>
+<t>
+   In the ABNF for chunked extensions, (bad) whitespace around ";" and "="
+   has been reintroduced. Whitespace was removed
+   in <xref target="RFC7230"/>, but that change was found to break existing
+   implementations.
+   (<xref target="chunked.extension"/>)
+</t>
+<t>
+   Trailer field semantics now transcend the specifics of chunked transfer coding.
+   The decoding algorithm for chunked (<xref target="decoding.chunked"/>) has
+   been updated to encourage storage/forwarding of trailer fields separately
+   from the header section, to only allow merging into the header section if
+   the recipient knows the corresponding field definition permits and defines
+   how to merge, and otherwise to discard the trailer fields instead of
+   merging. The trailer part is now called the trailer section to be more
+   consistent with the header section and more distinct from a body part.
+   (<xref target="chunked.trailer.section"/>)
+</t>
+<t>
+   Transfer coding parameters called "q" are disallowed in order to avoid
+   conflicts with the use of ranks in the <x:ref>TE</x:ref> header field.
+   (<xref target="transfer.coding.registry"/>)
+</t>
+</section>
+</section>
+
+<section title="Acknowledgements" anchor="acks" numbered="false">
+<t>
+  See <xref target="HTTP" x:rel="#acks"/>, which applies to this document as well.
+</t>
+</section>
+</back>
+</rfc>
\ No newline at end of file
diff --git a/test/fixtures/cache-tests/spec/script.mjs b/test/fixtures/cache-tests/spec/script.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/script.mjs
@@ -0,0 +1,79 @@
+import baseTests from '../tests/index.mjs'
+
+const SUITE = 'suite'
+const TEST = 'id'
+
+function populateLinks () {
+  const anchors = new AnchorMap()
+  baseTests.forEach(suite => {
+    if (suite.spec_anchors) {
+      suite.spec_anchors.forEach(anchor => {
+        anchors.push(anchor, [SUITE, suite.id])
+      })
+    }
+    suite.tests.forEach(test => {
+      if (test.spec_anchors) {
+        test.spec_anchors.forEach(anchor => {
+          anchors.push(anchor, [TEST, test.id])
+        })
+      }
+    })
+  })
+  for (const [anchor, ids] of anchors.map.entries()) {
+    adornSpecSection(anchor, ids)
+  }
+}
+
+function adornSpecSection (anchor, ids) {
+  const anchorNode = document.getElementById(anchor)
+  if (!anchorNode) {
+    console.log(`Anchor ${anchor} not found.`)
+    return
+  }
+  const headerNode = anchorNode.children[0]
+  const wrapper = document.createElement('span')
+  wrapper.classList.add('adornment')
+  const adornment = document.createTextNode('ℹ️')
+  wrapper.appendChild(adornment)
+  wrapper.addEventListener('click', function (event) {
+    event.preventDefault()
+    showTests(ids)
+    this.scrollIntoView(true)
+  })
+  headerNode.appendChild(wrapper)
+}
+
+function showTests (ids) {
+  // modify the spec HTML to make room for the test results
+  const specNode = document.getElementById('top')
+  specNode.classList.add('half')
+  const mainNode = document.querySelector('div[role="main"]')
+  mainNode.classList.remove('col-lg-8')
+  mainNode.classList.add('col-lg-11')
+  const tocNode = document.getElementById('sidebar')
+  tocNode.classList.remove('d-lg-block')
+  tocNode.classList.add('d-none')
+  const iframeNode = document.createElement('iframe')
+  iframeNode.id = 'resultsFrame'
+  const query = ids.map(id => `${id[0]}=${id[1]}`).join('&')
+  iframeNode.setAttribute('src', `/index.html?${query}&frame=1`)
+  document.body.appendChild(iframeNode)
+}
+
+class AnchorMap {
+  constructor () {
+    this.map = new Map()
+  }
+
+  push (k, v) {
+    const val = this.map.get(k)
+    if (val) {
+      val.push(v)
+      this.map.set(k, val)
+    } else {
+      this.map.set(k, [v])
+    }
+  }
+}
+
+populateLinks()
diff --git a/test/fixtures/cache-tests/spec/style.css b/test/fixtures/cache-tests/spec/style.css
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/spec/style.css
@@ -0,0 +1,23 @@
+
+.adornment {
+  padding-left: 1em;
+}
+
+#resultsFrame {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  height: 100vh;
+  width: 50vw;
+  z-index: 500;
+  background-color: white;
+  border: 0px;
+  border-left: 2px solid #666;
+  overflow-y: auto;
+}
+
+.half {
+  position: absolute;
+  width: 48vw;
+  left: 0;
+}
diff --git a/test/fixtures/cache-tests/test-browser.html b/test/fixtures/cache-tests/test-browser.html
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-browser.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Browser HTTP Caching Tests</title>
+    <link rel="stylesheet" href="/asset/style.css" type="text/css">
+    <script type="module">
+      import { runTests, getResults } from './test-engine/client/runner.mjs'
+      import { testUUIDs } from './test-engine/client/test.mjs'
+      import * as display from './test-engine/lib/display.mjs'
+      import baseTests from './tests/index.mjs'
+
+      runTests(baseTests, fetch, true)
+        .then(function() {
+          let resultsTarget = document.getElementById('results')
+          display.renderTestResults(baseTests, getResults(), testUUIDs, resultsTarget, true)
+        })
+        .then(function() {
+          let params = (new URL(location)).searchParams
+          let filename = 'results.json'
+          if (params.get('download')) {
+            filename = `${params.get('download')}.json`
+          }
+          let auto = false
+          if (params.get('auto')) {
+            auto = true
+          }
+          let downloadTarget = document.getElementById('download')
+          display.downloadTestResults(downloadTarget, filename, getResults(), auto)
+        })
+    </script>
+  </head>
+  <body>
+    <h1>Browser HTTP Caching Tests</h1>
+
+    <p class="warning">These tests are a work in progress. The reported results may be faulty, and
+    do not necessarily reflect the true capabilities of each cache. Furthermore, their primary
+    purpose is to inform revision of the <a
+    href="https://httpwg.org/http-core/draft-ietf-httpbis-cache-latest.html">HTTP caching
+    specification</a>; so, they should not be used evaluate or compare feature support, since the
+    specification itself might change. <strong>This is an open source project</strong>; to make
+    contributions, add your implementation's results, file issues or learn more, see <a
+    href="https://github.com/http-tests/cache-tests">the repository</a>.</p>
+
+    <p><i>Hover over failed tests for details. Click on <span class="hint">⚙︎</span> to copy the
+    test UUID (for finding it in the browser networking tab or proxy logs).</i></p>
+
+    <p><a id="download">Download results (JSON)</a></p>
+
+    <div id="results"></div>
+  </body>
+</html>
diff --git a/test/fixtures/cache-tests/test-browser.sh b/test/fixtures/cache-tests/test-browser.sh
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/test-browser.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+## Run tests against a local browser on OSX.
+
+set -euo pipefail
+
+PORT=8000
+DOWNLOADS=~/Downloads
+PIDFILE=/tmp/http-cache-test-server.pid
+
+function usage {
+  if [[ -n "${1}" ]]; then
+    echo "${1}"
+  fi
+  echo "Usage: $0 [ browser-name ... ]" >&2
+}
+
+function run {
+  BROWSERS=( "$@" )
+
+  # start test server
+  npm run --silent server --port=$PORT --pidfile=$PIDFILE &
+  trap cleanup EXIT
+  sleep 2
+
+  for browser in "${BROWSERS[@]}"
+  do
+    test_browser "${browser}"
+  done
+}
+
+function cleanup {
+  # stop test server
+  kill "$(cat $PIDFILE)" > /dev/null 2>&1
+  rm $PIDFILE
+}
+
+function test_browser {
+  BROWSER=${1}
+  URL="http://localhost:${PORT}/test-browser.html?auto=1&download=${BROWSER}"
+
+  case ${BROWSER} in
+    safari)
+      BROWSER_CMD="/Applications/Safari.app"
+      ;;
+    firefox)
+      BROWSER_CMD="/Applications/Firefox.app"
+      ;;
+    chrome)
+      BROWSER_CMD="/Applications/Google Chrome.app"
+      ;;
+    *)
+      usage "Browser ${BROWSER} not recognised."
+      return
+      ;;
+  esac
+
+  # remove target file
+  TARGET="${DOWNLOADS}/${BROWSER}.json"
+  rm -f "${TARGET}"
+
+  # run tests
+  open -g -a "${BROWSER_CMD}" "${URL}"
+
+  # wait for the target to be created
+  i=0
+  while [ ! -f "${TARGET}" ]
+  do
+    sleep 1
+    i=$((i+1))
+    if [ "$i" -gt "60" ] ; then
+      echo "Timeout." >&2
+      exit 1
+    fi
+  done
+
+  sleep 1
+  if [ -f "${TARGET}" ] ; then
+    mv "${TARGET}" results/
+  fi
+
+}
+
+OS=$(uname)
+if [[ "${OS}" != "Darwin" ]]; then
+  usage "This script must be run on Mac OSX."
+  exit 1
+fi
+
+if [[ $# -eq 0 ]]; then
+  run safari firefox chrome
+else
+  run "$@"
+fi
diff --git a/test/fixtures/cache-tests/test-docker.sh b/test/fixtures/cache-tests/test-docker.sh
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/test-docker.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+
+## Run tests against a local docker image with common proxy/caches.
+
+set -euo pipefail
+
+PIDFILE=/tmp/http-cache-test-server.pid
+
+ALL_PROXIES=(squid nginx apache trafficserver varnish caddy)
+DOCKER_PORTS=""
+for PORT in {8001..8006}; do
+  DOCKER_PORTS+="-p 127.0.0.1:${PORT}:${PORT} "
+done
+
+function usage {
+  if [[ -n "${1}" ]]; then
+    echo "${1}"
+  fi
+  echo "Usage: $0 [ -i test_id ] [ proxy... ]"
+}
+
+function run {
+  TEST_ID="${1}"
+  shift
+  PROXIES=( "$@" )
+  # start test server
+  npm run --silent server --port=8000 --pidfile=${PIDFILE} &
+
+  # run proxies container
+  docker run --name=tmp_proxies ${DOCKER_PORTS} -dt mnot/proxy-cache-tests host.docker.internal \
+    > /dev/null
+
+  trap cleanup EXIT
+
+  # give docker enough time to start
+  sleep 10
+
+  for proxy in "${PROXIES[@]}"
+  do
+    test_proxy "${proxy}" "${TEST_ID}"
+  done
+}
+
+function cleanup {
+  # stop docker containers
+  docker kill tmp_proxies > /dev/null
+  docker rm tmp_proxies > /dev/null
+
+  # stop test server
+  kill "$(cat ${PIDFILE})" > /dev/null 2>&1
+  rm ${PIDFILE}
+}
+
+function test_proxy {
+  PROXY=$1
+  PKG=$1
+  TEST_ID=$2
+  case ${PKG} in
+    squid)
+      PROXY_PORT=8001
+      ;;
+    nginx)
+      PROXY_PORT=8002
+      ;;
+    trafficserver)
+      PROXY_PORT=8003
+      ;;
+    apache)
+      PROXY_PORT=8004
+      PKG=apache2
+      ;;
+    varnish)
+      PROXY_PORT=8005
+      ;;
+    caddy)
+      PROXY_PORT=8006
+      ;;
+    *)
+      echo "Proxy ${PKG} not recognised."
+      exit 1
+      ;;
+  esac
+
+  echo "* ${PKG} $(docker container exec tmp_proxies /usr/bin/apt-cache show ${PKG} | grep Version)"
+
+  if [[ -z "${TEST_ID}" ]]; then
+    npm run --silent cli --base=http://localhost:${PROXY_PORT} > "results/${PROXY}.json"
+  else
+    npm run --silent cli --base=http://localhost:${PROXY_PORT} --id="${TEST_ID}"
+  fi
+}
+
+
+TEST_ID=""
+while getopts "h?i:" opt; do
+  case "${opt}" in
+    h)
+      usage
+      exit 0
+      ;;
+    i)
+      TEST_ID=$OPTARG
+      ;;
+    *)
+      usage
+      exit 1
+      ;;
+  esac
+done
+shift $((OPTIND-1))
+
+if [[ $# -eq 0 ]]; then
+  run "" "${ALL_PROXIES[@]}"
+else
+  run "${TEST_ID}" "${@}"
+fi
diff --git a/test/fixtures/cache-tests/test-engine/cli.mjs b/test/fixtures/cache-tests/test-engine/cli.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/cli.mjs
@@ -0,0 +1,44 @@
+import { runTests, getResults } from './client/runner.mjs'
+import { determineTestResult } from './lib/results.mjs'
+import { GREEN, NC } from './lib/defines.mjs'
+import fetch from 'node-fetch-with-proxy'
+import tests from '../tests/index.mjs'
+
+const baseUrl = process.env.npm_config_base || process.env.npm_package_config_base
+const testId = process.env.npm_config_id || process.env.npm_package_config_id
+
+let testsToRun
+if (testId !== '') {
+  console.log(`Running ${testId}`)
+  tests.forEach(suite => {
+    suite.tests.forEach(test => {
+      if (test.id === testId) {
+        test.dump = true
+        testsToRun = [{
+          name: suite.name,
+          id: suite.id,
+          description: suite.description,
+          tests: [test]
+        }]
+      }
+    })
+  })
+} else {
+  testsToRun = tests
+}
+
+await runTests(testsToRun, fetch, false, baseUrl).catch(err => {
+  console.error(err)
+  process.exit(1)
+})
+
+const results = getResults()
+
+if (testId !== '') {
+  console.log(`${GREEN}==== Results${NC}`)
+  const resultSymbol = determineTestResult(tests, testId, results, false)
+  const resultDetails = results[testId][1] || ''
+  console.log(`${resultSymbol[2]} - ${resultDetails}`)
+} else {
+  console.log(JSON.stringify(results, null, 2))
+}
diff --git a/test/fixtures/cache-tests/test-engine/client/config.mjs b/test/fixtures/cache-tests/test-engine/client/config.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/client/config.mjs
@@ -0,0 +1,22 @@
+export let fetch = null
+export let useBrowserCache = false
+export let baseUrl = ''
+export const requestTimeout = 10 // seconds
+
+export function setFetch (call) {
+  if (call !== undefined) {
+    if ('bind' in call) {
+      fetch = call.bind(fetch)
+    } else {
+      fetch = call
+    }
+  }
+}
+
+export function setUseBrowserCache (bool) {
+  if (bool !== undefined) useBrowserCache = bool
+}
+
+export function setBaseUrl (url) {
+  if (url !== undefined) baseUrl = url
+}
diff --git a/test/fixtures/cache-tests/test-engine/client/fetching.mjs b/test/fixtures/cache-tests/test-engine/client/fetching.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/client/fetching.mjs
@@ -0,0 +1,45 @@
+import * as config from './config.mjs'
+import { fixupHeader } from '../lib/header-fixup.mjs'
+
+export function init (idx, reqConfig, prevResp) {
+  const init = {
+    headers: []
+  }
+  if (!config.useBrowserCache) {
+    init.cache = 'no-store'
+    init.headers.push(['Pragma', 'foo']) // dirty hack for Fetch
+    init.headers.push(['Cache-Control', 'nothing-to-see-here']) // ditto
+  }
+  if ('request_method' in reqConfig) init.method = reqConfig.request_method
+  if ('request_headers' in reqConfig) init.headers = reqConfig.request_headers
+  if ('magic_ims' in reqConfig && reqConfig.magic_ims === true) {
+    for (let i = 0; i < init.headers.length; i++) {
+      const header = init.headers[i]
+      if (header[0].toLowerCase() === 'if-modified-since') {
+        init.headers[i] = fixupHeader(header, prevResp, reqConfig)
+      }
+    }
+  }
+  if ('name' in reqConfig) init.headers.push(['Test-Name', reqConfig.name])
+  if ('request_body' in reqConfig) init.body = reqConfig.request_body
+  if ('mode' in reqConfig) init.mode = reqConfig.mode
+  if ('credentials' in reqConfig) init.mode = reqConfig.credentials
+  if ('cache' in reqConfig) init.cache = reqConfig.cache
+  if ('redirect' in reqConfig) init.redirect = reqConfig.redirect
+  init.headers.push(['Test-ID', reqConfig.id])
+  init.headers.push(['Req-Num', (idx + 1).toString()])
+  return init
+}
+
+export function inflateRequests (test) {
+  const rawRequests = test.requests
+  const requests = []
+  for (let i = 0; i < rawRequests.length; i++) {
+    const reqConfig = rawRequests[i]
+    reqConfig.name = test.name
+    reqConfig.id = test.id
+    reqConfig.dump = test.dump
+    requests.push(reqConfig)
+  }
+  return requests
+}
diff --git a/test/fixtures/cache-tests/test-engine/client/runner.mjs b/test/fixtures/cache-tests/test-engine/client/runner.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/client/runner.mjs
@@ -0,0 +1,41 @@
+import * as config from './config.mjs'
+import { makeTest, testResults } from './test.mjs'
+
+
+export async function runTests (tests, myFetch, browserCache, base, chunkSize = 50) {
+  const testArray = []
+  config.setFetch(myFetch)
+  config.setBaseUrl(base)
+  config.setUseBrowserCache(browserCache)
+  tests.forEach(testSet => {
+    testSet.tests.forEach(test => {
+      if (test.id === undefined) throw new Error('Missing test id')
+      if (test.browser_only === true && !config.useBrowserCache === true) return
+      if (test.cdn_only === true && config.useBrowserCache === true) return
+      // note: still runs cdn tests on rev-proxy
+      if (test.browser_skip === true && config.useBrowserCache === true) return
+      testArray.push(test)
+    })
+  })
+  return runSome(testArray, chunkSize)
+}
+
+export function getResults () {
+  const ordered = {}
+  Object.keys(testResults).sort().forEach(key => {
+    ordered[key] = testResults[key]
+  })
+  return ordered
+}
+
+async function runSome (tests, chunkSize) {
+  let index = 0
+  function next () {
+    if (index < tests.length) {
+      const these = tests.slice(index, index + chunkSize).map(makeTest)
+      index += chunkSize
+      return Promise.all(these).then(next)
+    }
+  }
+  return next()
+}
diff --git a/test/fixtures/cache-tests/test-engine/client/test.mjs b/test/fixtures/cache-tests/test-engine/client/test.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/client/test.mjs
@@ -0,0 +1,300 @@
+import * as defines from '../lib/defines.mjs'
+import { fixupHeader } from '../lib/header-fixup.mjs'
+import * as utils from '../lib/utils.mjs'
+import * as config from './config.mjs'
+import * as clientUtils from './utils.mjs'
+import * as fetching from './fetching.mjs'
+const assert = utils.assert
+const setupCheck = clientUtils.setupCheck
+
+export const testUUIDs = {}
+export const testResults = {}
+
+export async function makeTest (test) {
+  const uuid = utils.token()
+  testUUIDs[test.id] = uuid
+  const requests = fetching.inflateRequests(test)
+  const responses = []
+  const fetchFunctions = []
+  for (let i = 0; i < requests.length; ++i) {
+    fetchFunctions.push({
+      code: idx => {
+        const reqConfig = requests[idx]
+        const reqNum = idx + 1
+        const url = clientUtils.makeTestUrl(uuid, reqConfig)
+        let prevRes
+        if (i > 0) {
+          prevRes = Object.fromEntries(responses[i - 1].headers)
+        }
+        const init = fetching.init(idx, reqConfig, prevRes)
+        const controller = new AbortController()
+        const timeout = setTimeout(() => {
+          controller.abort()
+        }, config.requestTimeout * 1000)
+        init.signal = controller.signal
+        if (test.dump === true) clientUtils.logRequest(url, init, reqNum)
+        return config.fetch(url, init)
+          .then(response => {
+            responses.push(response)
+            return checkResponse(test, requests, idx, response)
+          })
+          .finally(() => {
+            clearTimeout(timeout)
+          })
+      },
+      pauseAfter: 'pause_after' in requests[i]
+    })
+  }
+
+  let idx = 0
+  function runNextStep () {
+    if (fetchFunctions.length) {
+      const nextFetchFunction = fetchFunctions.shift()
+      if (nextFetchFunction.pauseAfter === true) {
+        return nextFetchFunction.code(idx++)
+          .then(clientUtils.pause)
+          .then(runNextStep)
+      } else {
+        return nextFetchFunction.code(idx++)
+          .then(runNextStep)
+      }
+    }
+  }
+
+  return clientUtils.putTestConfig(uuid, requests)
+    .catch(handleError)
+    .then(runNextStep)
+    .then(() => {
+      return clientUtils.getServerState(uuid)
+    })
+    .then(serverState => {
+      checkServerRequests(requests, responses, serverState)
+    })
+    .then(() => { // pass
+      if (test.id in testResults) throw new Error(`Duplicate test ${test.id}`)
+      testResults[test.id] = true
+    })
+    .catch(err => { // fail
+      if (test.id in testResults) throw new Error(`Duplicate test ${test.id}`)
+      testResults[test.id] = [(err.name || 'unknown'), err.message]
+    })
+}
+
+function checkResponse (test, requests, idx, response) {
+  const reqNum = idx + 1
+  const reqConfig = requests[idx]
+  const resNum = parseInt(response.headers.get('Server-Request-Count'))
+  if (test.dump === true) clientUtils.logResponse(response, reqNum)
+
+  // catch retries
+  if (response.headers.has('Request-Numbers')) {
+    const serverRequests = response.headers.get('Request-Numbers').split(' ').map(item => parseInt(item))
+    if (serverRequests.length !== new Set(serverRequests).size) {
+      assert(true, false, 'retry')
+    }
+  }
+
+  // check response type
+  if ('expected_type' in reqConfig) {
+    const typeSetup = setupCheck(reqConfig, 'expected_type')
+    if (reqConfig.expected_type === 'cached') {
+      if (response.status === 304 && isNaN(resNum)) { // some caches will not include the hdr
+        // pass
+      } else {
+        assert(typeSetup, resNum < reqNum, `Response ${reqNum} does not come from cache`)
+      }
+    }
+    if (reqConfig.expected_type === 'not_cached') {
+      assert(typeSetup, resNum === reqNum, `Response ${reqNum} comes from cache`)
+    }
+  }
+
+  // check response status
+  if ('expected_status' in reqConfig) {
+    assert(setupCheck(reqConfig, 'expected_status'),
+      response.status === reqConfig.expected_status,
+      `Response ${reqNum} status is ${response.status}, not ${reqConfig.expected_status}`)
+  } else if ('response_status' in reqConfig) {
+    assert(true, // response status is always setup
+      response.status === reqConfig.response_status[0],
+      `Response ${reqNum} status is ${response.status}, not ${reqConfig.response_status[0]}`)
+  } else if (response.status === 999) {
+    // special condition; the server thought it should have received a conditional request.
+    assert(setupCheck(reqConfig, 'expected_type'), false,
+      `Request ${reqNum} should have been conditional, but it was not.`)
+  } else {
+    assert(true, // default status is always setup
+      response.status === 200,
+      `Response ${reqNum} status is ${response.status}, not 200`)
+  }
+
+  // check response headers
+  if ('expected_response_headers' in reqConfig) {
+    const respPresentSetup = setupCheck(reqConfig, 'expected_response_headers')
+    reqConfig.expected_response_headers.forEach(header => {
+      if (typeof header === 'string') {
+        assert(respPresentSetup, response.headers.has(header),
+          `Response ${reqNum} ${header} header not present.`)
+      } else if (header.length > 2) {
+        assert(respPresentSetup, response.headers.has(header[0]),
+          `Response ${reqNum} ${header[0]} header not present.`)
+
+        const value = response.headers.get(header[0])
+        let msg, condition
+        if (header[1] === '=') {
+          const expected = response.headers.get(header[2])
+          condition = value === expected
+          msg = `match ${header[2]} (${expected})`
+        } else if (header[1] === '>') {
+          const expected = header[2]
+          condition = parseInt(value) > expected
+          msg = `be bigger than ${expected}`
+        } else {
+          throw new Error(`Unknown expected-header operator '${header[1]}'`)
+        }
+
+        assert(respPresentSetup, condition,
+          `Response ${reqNum} header ${header[0]} is ${value}, should ${msg}`)
+      } else {
+        const expectedValue = fixupHeader(
+          header, Object.fromEntries(response.headers), reqConfig)[1]
+        assert(respPresentSetup, response.headers.get(header[0]) === expectedValue,
+          `Response ${reqNum} header ${header[0]} is "${response.headers.get(header[0])}", not "${expectedValue}"`)
+      }
+    })
+  }
+  if ('expected_response_headers_missing' in reqConfig) {
+    const respMissingSetup = setupCheck(reqConfig, 'expected_response_headers_missing')
+    reqConfig.expected_response_headers_missing.forEach(header => {
+      if (typeof header === 'string') {
+        assert(respMissingSetup, !response.headers.has(header),
+          `Response ${reqNum} includes unexpected header ${header}: "${response.headers.get(header)}"`)
+      } else if (header.length === 2) {
+        if (response.headers.has(header[0]) && response.headers[header[0]]) {
+          const hdrValue = response.headers[header[0]]
+          assert(respMissingSetup, hdrValue.indexOf(header[1]) === -1, `Response ${reqNum} header ${header[0]} still has value "${hdrValue}"`)
+        }
+      } else {
+        throw new Error(`Unknown unexpected-header form '${header}'`)
+      }
+    })
+  }
+  return response.text().then(makeCheckResponseBody(test, reqConfig, response.status))
+}
+
+function makeCheckResponseBody (test, reqConfig, statusCode) {
+  return function checkResponseBody (resBody) {
+    if ('check_body' in reqConfig && reqConfig.check_body === false) {
+      return true
+    } else if ('expected_response_text' in reqConfig) {
+      if (reqConfig.expected_response_text !== null) {
+        assert(setupCheck(reqConfig, 'expected_response_text'),
+          resBody === reqConfig.expected_response_text,
+          `Response body is "${resBody}", not "${reqConfig.expected_response_text}"`)
+      }
+    } else if ('response_body' in reqConfig && reqConfig.response_body !== null) {
+      assert(true, // response_body is always setup
+        resBody === reqConfig.response_body,
+        `Response body is "${resBody}", not "${reqConfig.response_body}"`)
+    } else if (!defines.noBodyStatus.has(statusCode) && reqConfig.request_method !== 'HEAD') {
+      const uuid = testUUIDs[test.id]
+      assert(true, // no_body is always setup
+        resBody === uuid,
+        `Response body is "${resBody}", not "${uuid}"`)
+    }
+  }
+}
+
+function checkServerRequests (requests, responses, serverState) {
+  // compare a test's requests array against the server-side serverState
+  let testIdx = 0
+  for (let i = 0; i < requests.length; ++i) {
+    const expectedValidatingHeaders = []
+    const reqConfig = requests[i]
+    const response = responses[i]
+    const serverRequest = serverState[testIdx]
+    const reqNum = i + 1
+    const typeSetup = setupCheck(reqConfig, 'expected_type')
+    if ('expected_type' in reqConfig) {
+      if (reqConfig.expected_type === 'cached') continue // the server will not see the request
+      if (reqConfig.expected_type === 'not_cached') {
+        assert(typeSetup, serverRequest.request_num === reqNum, `Response ${reqNum} comes from cache (${serverRequest.request_num} on server)`)
+      }
+      if (reqConfig.expected_type === 'etag_validated') {
+        expectedValidatingHeaders.push('if-none-match')
+      }
+      if (reqConfig.expected_type === 'lm_validated') {
+        expectedValidatingHeaders.push('if-modified-since')
+      }
+    }
+    testIdx++ // only increment for requests the server sees
+    expectedValidatingHeaders.forEach(vhdr => {
+      assert(typeSetup, typeof (serverRequest) !== 'undefined', `request ${reqNum} wasn't sent to server`)
+      assert(typeSetup, Object.prototype.hasOwnProperty.call(serverRequest.request_headers, vhdr),
+        `request ${reqNum} doesn't have ${vhdr} header`)
+    })
+    if ('expected_request_headers' in reqConfig) {
+      const reqPresentSetup = setupCheck(reqConfig, 'expected_request_headers')
+      reqConfig.expected_request_headers.forEach(header => {
+        if (typeof header === 'string') {
+          const headerName = header.toLowerCase()
+          assert(reqPresentSetup, Object.prototype.hasOwnProperty.call(serverRequest.request_headers, headerName),
+            `Request ${reqNum} ${header} header not present.`)
+        } else {
+          const reqValue = serverRequest.request_headers[header[0].toLowerCase()]
+          assert(reqPresentSetup, reqValue === header[1],
+            `Request ${reqNum} header ${header[0]} is "${reqValue}", not "${header[1]}"`)
+        }
+      })
+    }
+    if ('expected_request_headers_missing' in reqConfig) {
+      const reqmPresentSetup = setupCheck(reqConfig, 'expected_request_headers_missing')
+      reqConfig.expected_request_headers_missing.forEach(header => {
+        if (typeof header === 'string') {
+          const headerName = header.toLowerCase()
+          assert(reqmPresentSetup, !Object.prototype.hasOwnProperty.call(serverRequest.request_headers, headerName),
+            `Request ${reqNum} ${header} header present.`)
+        } else {
+          const reqValue = serverRequest.request_headers[header[0].toLowerCase()]
+          assert(reqmPresentSetup, reqValue !== header[1],
+            `Request ${reqNum} header ${header[0]} is "${reqValue}"`)
+        }
+      })
+    }
+    if (typeof serverRequest !== 'undefined' && 'response_headers' in serverRequest) {
+      serverRequest.response_headers.forEach(header => {
+        if (config.useBrowserCache && defines.forbiddenResponseHeaders.has(header[0].toLowerCase())) {
+          // browsers prevent reading these headers through the Fetch API so we can't verify them
+          return
+        }
+        if (defines.skipResponseHeaders.has(header[0].toLowerCase())) {
+          // these just cause spurious failures
+          return
+        }
+        let received = response.headers.get(header[0])
+        // XXX: assumes that if a proxy joins headers, it'll separate them with a comma and exactly one space
+        if (Array.isArray(received)) {
+          received = received.join(', ')
+        }
+        if (Array.isArray(header[1])) {
+          header[1] = header[1].join(', ')
+        }
+        assert(true, // default headers is always setup
+          received === header[1],
+          `Response ${reqNum} header ${header[0]} is "${received}", not "${header[1]}"`)
+      })
+    }
+    if ('expected_method' in reqConfig) {
+      assert(
+        setupCheck(reqConfig, 'expected_method'),
+        serverRequest.request_method === reqConfig.expected_method,
+        `Request ${reqNum} had method ${serverRequest.request_method}, not ${reqConfig.expected_method}`
+      )
+    }
+  }
+}
+
+function handleError (err) {
+  console.log(new Error().stack)
+  console.error(`ERROR: ${err}`)
+}
diff --git a/test/fixtures/cache-tests/test-engine/client/utils.mjs b/test/fixtures/cache-tests/test-engine/client/utils.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/client/utils.mjs
@@ -0,0 +1,82 @@
+import * as config from './config.mjs'
+import * as utils from '../lib/utils.mjs'
+import * as defines from '../lib/defines.mjs'
+
+export function pause () {
+  return new Promise(function (resolve, reject) {
+    setTimeout(() => {
+      return resolve()
+    }, 3000)
+  })
+}
+
+export function makeTestUrl (uuid, reqConfig) {
+  let extra = ''
+  if ('filename' in reqConfig) {
+    extra += `/${reqConfig.filename}`
+  }
+  if ('query_arg' in reqConfig) {
+    extra += `?${reqConfig.query_arg}`
+  }
+  return `${config.baseUrl}/test/${uuid}${extra}`
+}
+
+const uninterestingHeaders = new Set(['date', 'expires', 'last-modified', 'content-length', 'content-type', 'connection', 'content-language', 'vary', 'mime-version'])
+
+export async function putTestConfig (uuid, requests) {
+  const init = {
+    method: 'PUT',
+    headers: [['content-type', 'application/json']],
+    body: JSON.stringify(requests)
+  }
+  return config.fetch(`${config.baseUrl}/config/${uuid}`, init)
+    .then(response => {
+      if (response.status !== 201) {
+        let headers = ''
+        response.headers.forEach((hvalue, hname) => { // for some reason, node-fetch reverses these
+          if (!uninterestingHeaders.has(hname.toLowerCase())) {
+            headers += `${hname}: ${hvalue}    `
+          }
+        })
+        throw new utils.SetupError({ message: `PUT config resulted in ${response.status} ${response.statusText} - ${headers}` })
+      }
+    })
+}
+
+export async function getServerState (uuid) {
+  return config.fetch(`${config.baseUrl}/state/${uuid}`)
+    .then(response => {
+      if (response.status === 200) {
+        return response.text()
+      }
+    }).then(text => {
+      if (text === undefined) return []
+      return JSON.parse(text)
+    })
+}
+
+export function setupCheck (reqConfig, memberName) {
+  return reqConfig.setup === true || ('setup_tests' in reqConfig && reqConfig.setup_tests.indexOf(memberName) > -1)
+}
+
+export function logRequest (url, init, reqNum) {
+  console.log(`${defines.GREEN}=== Client request ${reqNum}${defines.NC}`)
+  if ('method' in init) {
+    console.log(`    ${init.method} ${url}`)
+  } else {
+    console.log(`    GET ${url}`)
+  }
+  init.headers.forEach(header => {
+    console.log(`    ${header[0]}: ${header[1]}`)
+  })
+  console.log('')
+}
+
+export function logResponse (response, reqNum) {
+  console.log(`${defines.GREEN}=== Client response ${reqNum}${defines.NC}`)
+  console.log(`    HTTP ${response.status} ${response.statusText}`)
+  response.headers.forEach((hvalue, hname) => { // for some reason, node-fetch reverses these
+    console.log(`    ${hname}: ${hvalue}`)
+  })
+  console.log('')
+}
diff --git a/test/fixtures/cache-tests/test-engine/export.mjs b/test/fixtures/cache-tests/test-engine/export.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/export.mjs
@@ -0,0 +1,18 @@
+import fs from 'fs'
+
+import Ajv from 'ajv'
+
+import tests from '../tests/index.mjs'
+
+if (process.argv[2] === 'validate') {
+  const ajv = new Ajv()
+  const schema = JSON.parse(fs.readFileSync('test-engine/lib/testsuite-schema.json', 'utf8'))
+  const validate = ajv.compile(schema)
+  const valid = validate(tests)
+  if (!valid) {
+    console.log(validate.errors)
+    process.exit(1)
+  }
+} else {
+  console.log(JSON.stringify(tests, null, 2))
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/defines.mjs b/test/fixtures/cache-tests/test-engine/lib/defines.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/defines.mjs
@@ -0,0 +1,28 @@
+export const noBodyStatus = new Set([204, 304])
+
+export const dateHeaders = new Set(['date', 'expires', 'last-modified', 'if-modified-since', 'if-unmodified-since'])
+
+export const locationHeaders = new Set(['location', 'content-location'])
+
+// https://fetch.spec.whatwg.org/#forbidden-response-header-name
+export const forbiddenResponseHeaders = new Set(['set-cookie', 'set-cookie2'])
+
+// headers to skip when checking response_headers (not expected)
+export const skipResponseHeaders = new Set(['date'])
+
+// colours for console
+export const RED = '\x1b[31m'
+export const GREEN = '\x1b[32m'
+export const BLUE = '\x1b[34m'
+export const NC = '\x1b[0m'
+
+// mime types for server
+export const mimeTypes = {
+  html: 'text/html',
+  jpeg: 'image/jpeg',
+  jpg: 'image/jpeg',
+  png: 'image/png',
+  js: 'application/javascript',
+  mjs: 'application/javascript',
+  css: 'text/css'
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/display.mjs b/test/fixtures/cache-tests/test-engine/lib/display.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/display.mjs
@@ -0,0 +1,153 @@
+/* global Blob marked */
+
+import '../../asset/marked.min.js'
+import { Liquid } from '../../asset/liquid.browser.esm.mjs'
+import { modalOpen } from './modal.mjs'
+
+import { determineTestResult, resultTypes } from './results.mjs'
+
+const templateEngine = new Liquid({ root: 'test-engine/lib/tpl', extname: '.liquid', cache: true })
+templateEngine.registerFilter('typeof', v => typeof (v))
+templateEngine.registerFilter('toLocaleString', v => v.toLocaleString())
+templateEngine.registerFilter('skipHeaders', v => {
+  if (v) {
+    return v.filter(hdr => hdr.length < 3 || hdr[2] !== false)
+  } else {
+    return []
+  }
+})
+
+export function downloadTestResults (target, fileName, data, auto) {
+  const dataBlob = new Blob([JSON.stringify(data, null, 2)], { type: 'text/json' })
+  target.setAttribute('href', window.URL.createObjectURL(dataBlob))
+  target.setAttribute('download', fileName)
+  target.style.display = 'inherit'
+  if (auto) {
+    target.click()
+  }
+}
+
+export function renderTestResults (testSuites, testResults, testUUIDs, target, useBrowserCache) {
+  let totalTests = 0
+  let totalPassed = 0
+  testSuites.forEach(testSuite => {
+    const headerElement = document.createElement('h3')
+    target.appendChild(headerElement)
+    const headerText = document.createTextNode(testSuite.name)
+    headerElement.appendChild(headerText)
+    const listElement = document.createElement('ul')
+    const resultList = target.appendChild(listElement)
+    let tests = 0
+    let passed = 0
+    testSuite.tests.forEach(test => {
+      if (test.browser_only === true && !useBrowserCache === true) return
+      if (test.cdn_only === true && useBrowserCache === true) return
+      if (test.browser_skip === true && useBrowserCache === true) return
+      test.suiteName = testSuite.name
+      const testElement = resultList.appendChild(document.createElement('li'))
+      testElement.appendChild(showTestResult(testSuites, test.id, testResults))
+      testElement.appendChild(showTestName(test, testUUIDs[test.id]))
+      tests++
+      if (testResults[test.id] === true) {
+        passed++
+      }
+    })
+    const summaryElement = document.createElement('p')
+    const suiteSummary = target.appendChild(summaryElement)
+    suiteSummary.appendChild(document.createTextNode(tests + ' tests, ' + passed + ' passed.'))
+    totalTests += tests
+    totalPassed += passed
+  })
+  const totalElement = document.createElement('p')
+  const totalSummary = target.appendChild(totalElement)
+  const totalText = document.createTextNode('Total ' + totalTests + ' tests, ' + totalPassed + ' passed.')
+  totalSummary.appendChild(totalText)
+}
+
+export function showTestName (test, uuid) {
+  const wrapper = document.createElement('span')
+  const span = document.createElement('span')
+  span.setAttribute('class', 'clickhint')
+  span.innerHTML = marked.parse(test.name).slice(3, -5)
+  span.addEventListener('click', event => {
+    copyTextToClipboard(test.id)
+    showTestDetails(test)
+  })
+  wrapper.appendChild(span)
+
+  if (uuid) {
+    const uuidLinkElement = document.createElement('a')
+    uuidLinkElement.appendChild(document.createTextNode('⚙︎'))
+    uuidLinkElement.setAttribute('class', 'uuid')
+    uuidLinkElement.addEventListener('click', event => {
+      copyTextToClipboard(uuid)
+    })
+    uuidLinkElement.title = 'Test UUID (click to copy)'
+    wrapper.appendChild(uuidLinkElement)
+  }
+  return wrapper
+}
+
+export function showKey (element) {
+  const spans = element.getElementsByClassName('fa')
+  for (const span of spans) {
+    const kind = span.getAttribute('data-kind')
+    const styling = resultTypes[kind]
+    const contentNode = document.createTextNode(styling[0])
+    span.style.color = styling[1]
+    span.appendChild(contentNode)
+  }
+}
+
+export function showTestResult (testSuites, testId, testResults) {
+  const result = testResults[testId]
+  const resultValue = determineTestResult(testSuites, testId, testResults)
+  const resultNode = document.createTextNode(` ${resultValue[0]} `)
+  const span = document.createElement('span')
+  span.className = 'fa'
+  span.style.color = resultValue[1]
+  span.appendChild(resultNode)
+  if (result && typeof (result[1]) === 'string') {
+    span.title = result[1]
+  }
+  return span
+}
+
+export function showTestDetails (test) {
+  templateEngine
+    .renderFile('explain-test', { test })
+    .then(result => {
+      console.log(result)
+      const html = marked.parse(result)
+      modalOpen(html)
+    })
+    .catch(err => {
+      console.log(`Template error: ${err}`)
+    })
+}
+
+function copyTextToClipboard (text) {
+  const textArea = document.createElement('textarea')
+  textArea.style.position = 'fixed'
+  textArea.style.top = 0
+  textArea.style.left = 0
+  textArea.style.width = '2em'
+  textArea.style.height = '2em'
+  textArea.style.padding = 0
+  textArea.style.border = 'none'
+  textArea.style.outline = 'none'
+  textArea.style.boxShadow = 'none'
+  textArea.style.background = 'transparent'
+  textArea.value = text
+  document.body.appendChild(textArea)
+  textArea.focus()
+  textArea.select()
+  try {
+    const successful = document.execCommand('copy')
+    const msg = successful ? 'successful' : 'unsuccessful'
+    console.log(`Copying text "${text}" was ${msg}`)
+  } catch (err) {
+    console.log('Unable to copy')
+  }
+  document.body.removeChild(textArea)
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/header-fixup.mjs b/test/fixtures/cache-tests/test-engine/lib/header-fixup.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/header-fixup.mjs
@@ -0,0 +1,28 @@
+import { locationHeaders, dateHeaders } from './defines.mjs'
+import { httpDate } from './utils.mjs'
+
+export function fixupHeader (header, respHeaders, reqConfig) {
+  const headerName = header[0].toLowerCase()
+
+  // Date headers
+  const serverNow = parseInt(respHeaders['server-now'])
+  if (dateHeaders.has(headerName) && Number.isInteger(header[1])) {
+    let format
+    if ('rfc850date' in reqConfig && reqConfig.rfc850date.includes(headerName)) {
+      format = 'rfc850'
+    }
+    header[1] = httpDate(serverNow, header[1], format)
+  }
+
+  // Location headers
+  const baseUrl = respHeaders['server-base-url']
+  if (locationHeaders.has(headerName) && reqConfig.magic_locations) {
+    if (header[1]) {
+      header[1] = `${baseUrl}/${header[1]}`
+    } else {
+      header[1] = `${baseUrl}`
+    }
+  }
+
+  return header
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/modal.mjs b/test/fixtures/cache-tests/test-engine/lib/modal.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/modal.mjs
@@ -0,0 +1,27 @@
+export function modalOpen (content) {
+  let modal = document.getElementById('modal')
+  if (!modal) {
+    modal = document.createElement('div')
+    modal.classList.add('modal')
+    modal.id = 'modal'
+  }
+  modal.classList.add('modal-open')
+  modal.innerHTML = content
+  const closeButton = document.createElement('button')
+  const closeText = document.createTextNode('❎')
+  closeButton.appendChild(closeText)
+  closeButton.classList.add('modal-exit')
+  closeButton.addEventListener('click', function (event) {
+    event.preventDefault()
+    modal.classList.remove('modal-open')
+  })
+  modal.appendChild(closeButton)
+  document.body.appendChild(modal)
+  document.onkeydown = function (evt) {
+    evt = evt || window.event
+    if (evt.key === 'Escape' || evt.key === 'Esc') {
+      modal.classList.remove('modal-open')
+      document.onkeydown = function () {}
+    }
+  }
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/results.mjs b/test/fixtures/cache-tests/test-engine/lib/results.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/results.mjs
@@ -0,0 +1,73 @@
+export const resultTypes = {
+  untested: ['-', '', '-'],
+  pass: ['\uf058', '#1aa123', '✅'],
+  fail: ['\uf057', '#c33131', '⛔️'],
+  optional_fail: ['\uf05a', '#bbbd15', '⚠️'],
+  yes: ['\uf055', '#999696', 'Y'],
+  no: ['\uf056', '#999696', 'N'],
+  setup_fail: ['\uf059', '#4c61ae', '🔹'],
+  harness_fail: ['\uf06a', '#4c61ae', '⁉️'],
+  dependency_fail: ['\uf192', '#b4b2b2', '⚪️'],
+  retry: ['\uf01e', '#4c61ae', '↻']
+}
+const passTypes = [resultTypes.pass, resultTypes.yes]
+
+export function determineTestResult (testSuites, testId, testResults, honorDependencies = true) {
+  const test = testLookup(testSuites, testId)
+  const result = testResults[testId]
+  if (result === undefined) {
+    return resultTypes.untested
+  }
+  if (honorDependencies && test.depends_on !== undefined) {
+    for (const dependencyId of test.depends_on) {
+      if (!passTypes.includes(determineTestResult(testSuites, dependencyId, testResults))) {
+        return resultTypes.dependency_fail
+      }
+    }
+  }
+  if (result[0] === 'Setup') {
+    if (result[1] === 'retry') {
+      return resultTypes.retry
+    } else {
+      return resultTypes.setup_fail
+    }
+  }
+  if (result === false && result[0] !== 'Assertion') {
+    return resultTypes.harness_fail
+  }
+  if (result[0] === 'AbortError') {
+    return resultTypes.harness_fail
+  }
+  if (test.kind === 'required' || test.kind === undefined) {
+    if (result === true) {
+      return resultTypes.pass
+    } else {
+      return resultTypes.fail
+    }
+  } else if (test.kind === 'optimal') {
+    if (result === true) {
+      return resultTypes.pass
+    } else {
+      return resultTypes.optional_fail
+    }
+  } else if (test.kind === 'check') {
+    if (result === true) {
+      return resultTypes.yes
+    } else {
+      return resultTypes.no
+    }
+  } else {
+    throw new Error(`Unrecognised test kind ${test.kind}`)
+  }
+}
+
+export function testLookup (testSuites, testId) {
+  for (const testSuite of testSuites) {
+    for (const test of testSuite.tests) {
+      if (test.id === testId) {
+        return test
+      }
+    }
+  }
+  throw new Error(`Cannot find test ${testId}`)
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/summary.mjs b/test/fixtures/cache-tests/test-engine/lib/summary.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/summary.mjs
@@ -0,0 +1,178 @@
+/* global fetch marked */
+
+import '../../asset/marked.min.js'
+import * as display from './display.mjs'
+import { testLookup } from './results.mjs'
+
+export function loadResults (index) {
+  return Promise.all(index.map(item =>
+    fetch(`results/${item.file}`)
+      .then(response => {
+        return response.json()
+      })
+      .then(results => {
+        item.results = results
+        return item
+      }
+      ))
+  )
+}
+
+export function showResults (target, testSuites, results, testIds, suiteIds) {
+  const isDefault = testIds.length === 0 && suiteIds.length === 0
+  testSuites.forEach(testSuite => {
+    const selectedTests = []
+    const suiteTestIds = []
+    testSuite.tests.forEach(test => {
+      if (isDefault || suiteIds.includes(testSuite.id)) {
+        selectedTests.push(test)
+        suiteTestIds.push(test.id)
+      }
+      if (isDefault === 0 || testIds.includes(test.id)) {
+        if (!suiteTestIds.includes(test.id)) {
+          selectedTests.push(test)
+        }
+      }
+    })
+    if (selectedTests.length) {
+      showHeader(testSuite, results).forEach(row => {
+        target.appendChild(row)
+      })
+      selectedTests.forEach(test => {
+        const result = showTest(testSuites, test.id, results)
+        if (target.childElementCount % 2) {
+          result.setAttribute('class', 'shade')
+        }
+        target.appendChild(result)
+      })
+    }
+  })
+}
+
+export function showToC (target, testSuites) {
+  testSuites.forEach(testSuite => {
+    const suiteLink = document.createElement('a')
+    suiteLink.href = '#' + testSuite.id
+    suiteLink.appendChild(document.createTextNode(testSuite.name))
+    const suiteLi = document.createElement('li')
+    suiteLi.appendChild(suiteLink)
+    target.appendChild(suiteLi)
+  })
+}
+
+function showHeader (testSuite, results) {
+  const rows = []
+  const numCols = results.length + 2
+  const blankRow = tableRow()
+  blankRow.appendChild(emptyCell(numCols))
+  rows.push(blankRow)
+  const headerRow = tableRow()
+  headerRow.appendChild(tableCell('th', '\xa0', 'name category'))
+  const headerLink = document.createElement('a')
+  headerLink.href = '#' + testSuite.id
+  headerLink.appendChild(document.createTextNode(testSuite.name))
+  const firstHeader = tableCell('th', headerLink, 'name category')
+  firstHeader.id = testSuite.id
+  headerRow.appendChild(firstHeader)
+  results.forEach(implementation => {
+    headerRow.appendChild(tableCell('th', implementation.name, 'category', implementation.version, implementation.link))
+  })
+  rows.push(headerRow)
+  if (testSuite.description !== undefined) {
+    const descriptionRow = tableRow()
+    const drCells = emptyCell(numCols)
+    drCells.innerHTML = marked.parse(testSuite.description).slice(3, -5)
+    descriptionRow.appendChild(drCells)
+    rows.push(descriptionRow)
+  }
+  return rows
+}
+
+function showTest (testSuites, testId, results) {
+  const test = testLookup(testSuites, testId)
+  const testRow = tableRow()
+  testRow.appendChild(tableCell('td', testSelector(test.id)))
+  testRow.appendChild(tableCell('th', display.showTestName(test), 'name'))
+  results.forEach(implementation => {
+    testRow.appendChild(
+      tableCell('th', display.showTestResult(testSuites, test.id, implementation.results)))
+  })
+  return testRow
+}
+
+function tableRow (CssClass) {
+  const rowElement = document.createElement('tr')
+  if (CssClass) {
+    rowElement.setAttribute('class', CssClass)
+  }
+  return rowElement
+}
+
+function tableCell (cellType, content, CssClass, hint, link, colspan) {
+  const cellElement = document.createElement(cellType)
+  if (CssClass) {
+    cellElement.setAttribute('class', CssClass)
+  }
+  if (colspan) {
+    cellElement.colSpan = colspan
+  }
+  let contentNode
+  if (typeof (content) === 'string') {
+    contentNode = document.createTextNode(content)
+  } else {
+    contentNode = content
+  }
+  if (link) {
+    const linkElement = document.createElement('a')
+    linkElement.setAttribute('href', link)
+    linkElement.appendChild(contentNode)
+    cellElement.appendChild(linkElement)
+  } else {
+    cellElement.appendChild(contentNode)
+  }
+  if (hint) {
+    cellElement.title = hint
+  }
+  return cellElement
+}
+
+function testSelector (testId) {
+  const checkbox = document.createElement('input')
+  checkbox.type = 'checkbox'
+  checkbox.name = 'id'
+  checkbox.value = testId
+  checkbox.style.display = 'none'
+  checkbox.setAttribute('class', 'select')
+  return checkbox
+}
+
+export function selectClickListen () {
+  const select = document.getElementById('select')
+  select.addEventListener('click', selectClick, {
+    once: true
+  })
+}
+
+function selectClick () {
+  const selectBoxes = document.getElementsByClassName('select')
+  for (const selectBox of selectBoxes) {
+    selectBox.style.display = 'inherit'
+  }
+  const submit = document.createElement('input')
+  submit.type = 'submit'
+  submit.value = 'Show only selected tests'
+  const select = document.getElementById('select')
+  select.replaceWith(submit)
+}
+
+export function selectClearShow () {
+  const clear = document.createElement('a')
+  clear.href = '?'
+  clear.appendChild(document.createTextNode('Clear selections'))
+  const select = document.getElementById('select')
+  select.replaceWith(clear)
+}
+
+function emptyCell (numCols = 1) {
+  return tableCell('td', '\xa0', undefined, undefined, undefined, numCols)
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/testsuite-schema.json b/test/fixtures/cache-tests/test-engine/lib/testsuite-schema.json
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/testsuite-schema.json
@@ -0,0 +1,445 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "http://cache-tests.fyi/testsuite-schema.json",
+  "title": "Cache Tests",
+  "description": "A list of test suites",
+  "type": "array",
+  "items": {
+    "description": "A test suite",
+    "type": "object",
+    "required": [ "name", "id", "tests"],
+    "additionalProperties": false,
+    "properties": {
+      "name": {
+        "description": "The name of the suite",
+        "type": "string"
+      },
+      "id": {
+        "description": "A unique identifier for the suite",
+        "type": "string"
+      },
+      "description": {
+        "description": "A Markdown description of the suite",
+        "type": "string"
+      },
+      "spec_anchors": {
+        "description": "Anchors in the HTTP caching spec that this suite applies to",
+        "type": "array",
+        "items": {
+          "type": "string"
+        }
+      },
+      "tests": {
+        "description": "An array of tests in the suite",
+        "type": "array",
+        "items": {
+          "description": "A test",
+          "type": "object",
+          "additionalProperties": false,
+          "required": ["name", "id", "requests"],
+          "properties": {
+            "name": {
+              "description": "The test name; can contain Markdown.",
+              "type": "string"
+            },
+            "id": {
+              "$ref": "#/definitions/test-id"
+            },
+            "description": {
+              "description": "A longer, Markdown description of the test",
+              "type": "string"
+            },
+            "kind": {
+              "description": "The kind of test",
+              "type": "string",
+              "enum": ["required", "optimal", "check"]
+            },
+            "spec_anchors": {
+              "description": "Anchors in the HTTP caching spec that this test applies to",
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            },
+            "requests": {
+              "description": "An array of requests",
+              "type": "array",
+              "items": {
+                "description": "A request to send in the test",
+                "type": "object",
+                "additionalProperties": false,
+                "properties": {
+                  "request_method": {
+                    "description": "the HTTP method to be used",
+                    "type": "string"
+                  },
+                  "request_headers": {
+                    "description": "headers to emit in the request",
+                    "type": "array",
+                    "items": {
+                      "type": "array",
+                      "additionalItems": false,
+                      "items": [
+                        {
+                          "$ref": "#/definitions/field-name"
+                        },
+                        {
+                          "$ref": "#/definitions/magic-field-value"
+                        }
+                      ]
+                    }
+                  },
+                  "request_body": {
+                    "description": "the HTTP request body to be used",
+                    "type": "string"
+                  },
+                  "query_arg": {
+                    "description": "query arguments to add to the URL",
+                    "type": "string"
+                  },
+                  "filename": {
+                    "description": "filename to add to the URL",
+                    "type": "string"
+                  },
+                  "mode": {
+                    "description": "the mode value to pass to fetch()",
+                    "type": "string",
+                    "enum": ["same-origin", "no-cors", "navigate", "websocket"]
+                  },
+                  "credentials": {
+                    "description": "the credentials value to pass to fetch()",
+                    "type": "string",
+                    "enum": ["omit", "same-origin", "include"]
+                  },
+                  "cache": {
+                    "description": "the cache value to pass to fetch()",
+                    "type": "string",
+                    "enum": ["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"]
+                  },
+                  "redirect": {
+                    "description": "the redirect value to pass to fetch()",
+                    "type": "string",
+                    "enum": ["follow", "error", "manual"]
+                  },
+                  "pause_after": {
+                    "description": "Whether to pause for three seconds after the request completes",
+                    "type": "boolean"
+                  },
+                  "disconnect": {
+                    "description": "Whether to disconnect the client when receiving thsi request",
+                    "type": "boolean"
+                  },
+                  "magic_locations": {
+                    "description": "Whether to rewrite Location and Content-Location to full URLs",
+                    "type": "boolean"
+                  },
+                  "magic_ims": {
+                    "description": "Whether to rewrite If-Modified-Since to a delta from the previous Last-Modified",
+                    "type": "boolean"
+                  },
+                  "rfc850date": {
+                    "description": "Header names to use RFC850 format on when converting dates",
+                    "type": "array",
+                    "items": [
+                      {
+                        "$ref": "#/definitions/date-headers"
+                      }
+                    ]
+                  },
+                  "response_status": {
+                    "description": "HTTP status code and phrase to return from origin",
+                    "type": "array",
+                    "items": [
+                      {
+                        "$ref": "#/definitions/status-code"
+                      },
+                      {
+                        "description": "status phrase",
+                        "type": "string"
+                      }
+                    ]
+                  },
+                  "response_headers": {
+                    "description": "Response header fields to be returned from origin",
+                    "type": "array",
+                    "items": {
+                      "anyOf": [
+                        {
+                          "description": "name and value",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "oneOf": [
+                                { "type": "string" },
+                                { "type": "integer" }
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "description": "name and value with control over checking",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "$ref": "#/definitions/magic-field-value"
+                            },
+                            {
+                              "type": "boolean"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  },
+                  "response_body": {
+                    "description": "Response body to be returned from origin; defaults to the test identifier",
+                    "$ref": "#/definitions/response-or-null"
+                  },
+                  "check_body": {
+                    "description": "Whether to check the response body on the client",
+                    "type": "boolean"
+                  },
+                  "expected_type": {
+                    "description": "What the test result is expected to be",
+                    "type": "string",
+                    "enum": ["cached", "not_cached", "lm_validated", "etag_validated"]
+                  },
+                  "expected_method": {
+                    "description": "Expected request method received by the server",
+                    "type": "string"
+                  },
+                  "expected_status": {
+                    "description": "Expected response status received by the client",
+                    "$ref": "#/definitions/status-code"
+                  },
+                  "expected_request_headers": {
+                    "description": "Request headers to check for on the server",
+                    "type": "array",
+                    "items": {
+                      "anyOf": [
+                        {
+                          "$ref": "#/definitions/field-name"
+                        },
+                        {
+                          "description": "name and value",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "description": "field value",
+                              "type": "string"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  },
+                  "response_pause": {
+                    "description": "Pause the response body by the server",
+                    "type": "integer"
+                  },
+                  "expected_request_headers_missing": {
+                    "description": "Request headers to check for absence on the server",
+                    "type": "array",
+                    "items": {
+                      "anyOf": [
+                        {
+                          "$ref": "#/definitions/field-name"
+                        },
+                        {
+                          "description": "name and value",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "description": "field value",
+                              "type": "string"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  },
+                  "expected_response_headers": {
+                    "description": "Response headers to check for on the client",
+                    "type": "array",
+                    "items": {
+                      "anyOf": [
+                        {
+                          "$ref": "#/definitions/field-name"
+                        },
+                        {
+                          "description": "name and value",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "$ref": "#/definitions/magic-field-value"
+                            }
+                          ]
+                        },
+                        {
+                          "description": "check two headers have the same value",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "const": "="
+                            },
+                            {
+                              "description": "field name to check against",
+                              "type": "string"
+                            }
+                          ]
+                        },
+                        {
+                          "description": "header value is greater than an integer",
+                          "type": "array",
+                          "additionalItems": false,
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "const": ">"
+                            },
+                            {
+                              "description": "integer to check against",
+                              "type": "integer"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  },
+                  "expected_response_headers_missing": {
+                    "description": "Response headers to check are missing on the client",
+                    "type": "array",
+                    "items": {
+                      "oneOf": [
+                        {
+                          "$ref": "#/definitions/field-name"
+                        },
+                        {
+                          "description": "name and value",
+                          "type": "array",
+                          "items": [
+                            {
+                              "$ref": "#/definitions/field-name"
+                            },
+                            {
+                              "description": "field value substring",
+                              "type": "string"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  },
+                  "expected_response_text": {
+                    "description": "Expected response body received by the client",
+                    "$ref": "#/definitions/response-or-null"
+                  },
+                  "setup": {
+                    "description": "Whether this is a setup request; failures don't mean the actual test failed",
+                    "type": "boolean"
+                  },
+                  "setup_tests": {
+                    "description": "List of checks that are considered setup",
+                    "type": "array",
+                    "items": {
+                      "type": "string",
+                      "enum": ["expected_type", "expected_method", "expected_status", "expected_response_headers", "expected_response_text", "expected_request_headers"]
+                    }
+                  }
+                }
+              }
+            },
+            "browser_only": {
+              "description": "Whether the test will only run on browsers",
+              "type": "boolean"
+            },
+            "cdn_only": {
+              "description": "Whether the test will only run on CDN caches",
+              "type": "boolean"
+            },
+            "browser_skip": {
+              "description": "Whether the test will skip browsers",
+              "type": "boolean"
+            },
+            "depends_on": {
+              "description": "List of Test IDs that this test depends on",
+              "type": "array",
+              "items": {
+                "$ref": "#/definitions/test-id"
+              }
+            }
+          }
+        }
+      }
+    }
+  },
+  "definitions": {
+    "field-name": {
+      "description": "HTTP header field name",
+      "type": "string",
+      "pattern": "^[a-zA-Z0-9-_]+$"
+    },
+    "magic-field-value": {
+      "oneOf": [
+        {
+          "type": "string"
+        },
+        {
+          "type": "integer"
+        }
+      ]
+    },
+    "test-id": {
+      "description": "A short identifer for a test",
+      "type": "string"
+    },
+    "response-or-null": {
+      "oneOf": [
+        {
+          "description": "The text of the response",
+          "type": "string"
+        },
+        {
+          "description": "Do not check the response",
+          "type": "null"
+        }
+      ]
+    },
+    "status-code": {
+      "description": "HTTP response status code",
+      "type": "integer",
+      "minimum": 100,
+      "maximum": 599
+    },
+    "date-headers": {
+      "type": "string",
+      "enum": ["date", "if-modified-since", "last-modified", "expires"]
+    }
+  }
+}
diff --git a/test/fixtures/cache-tests/test-engine/lib/tpl/checks.liquid b/test/fixtures/cache-tests/test-engine/lib/tpl/checks.liquid
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/tpl/checks.liquid
@@ -0,0 +1,52 @@
+
+{%- if request.setup -%}
+  {%- assign setup_prop = '' -%}
+{%- else -%}
+  {%- assign setup_prop = ' _(Failure will be considered a test setup issue)_' -%}
+{%- endif %}
+
+### The following checks will be performed:
+
+{%- if request.expected_type %}
+- The client will check that this response {% case request.expected_type %}{% when "cached" %}is cached{% when "not_cached" %}is not cached{% when "lm_validated" %}is validated using `Last-Modified`{% when "etag_validated" %}is validated using `ETag`{% endcase %} {% if test.setup_tests contains "expected_type" %}{{ setup_prop }}{% endif %}{% endif -%}
+
+{%- if request.expected_method %}
+- The server will check that the request method is `{{ request.expected_method }}`.{% endif -%}
+
+{%- if request.expected_request_headers.size > 0 %}
+- The server will check that the following request headers (and values, when specified) are present{% if test.setup_tests contains "expected_request_headers" %}{{ setup_prop }}{% endif %}:
+{%- render 'header-list' with request.expected_request_headers as headers %}{% endif -%}
+
+{%- if request.expected_request_headers_missing.size > 0 %}
+- The server will check that the following request headers (and values, when specified) are absent{% if test.setup_tests contains "expected_request_headers_missing" %}{{ setup_prop }}{% endif %}:
+{%- render 'header-list' with request.expected_request_headers_missing as headers %}{% endif -%}
+
+{%- if request.expected_status %}
+- The client will check that the status code is `{{ request.expected_status }}` {% if test.setup_tests contains "expected_status" %}{{ setup_prop }}{% endif %}
+{%- elsif request.response_status %}
+- The client will check that the status code is `{{ request.response_status | join: " " }}`{{ setup_prop }}
+{%- else %}
+- The client will check that the status code is `200 OK`{{ setup_prop }}
+{%- endif -%}
+
+{%- assign response_headers = request.response_headers | skipHeaders -%}
+{%- if request.expected_response_headers.size > 0 or response_headers.size > 0 %}
+- The client will check that the following response headers (and values, when specified) are present{% if test.setup_tests contains "expected_response_headers" %}{{ setup_prop }}{% endif -%}:
+{%- render 'header-list' with request.expected_response_headers as headers -%}
+{%- render 'header-list' with response_headers as headers %}{% endif -%}
+
+{%- if request.expected_response_headers_missing.size > 0 %}
+- The client will check that the following response headers (and values, when specified) are missing:
+{%- render 'header-list' with request.expected_response_headers_missing as headers %}{% endif -%}
+
+{%- if request.check_body != false %}
+- The client will check the body
+{%- if request.expected_response text -%}
+, expecting: `{{ request.expected_response_text }}` {% if test.setup_tests contains "expected_response_text" %}{{ setup_prop }}{% endif %}
+{%- else -%}
+, expecting the generated response body.
+{%- endif -%}
+{%- endif -%}
+
+
+
diff --git a/test/fixtures/cache-tests/test-engine/lib/tpl/explain-test.liquid b/test/fixtures/cache-tests/test-engine/lib/tpl/explain-test.liquid
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/tpl/explain-test.liquid
@@ -0,0 +1,90 @@
+# {{ test.name }}
+
+Test ID: `{{ test.id }}`
+
+{% case test.kind %}
+{%- when "optimal" -%}This is an optional test for cache efficiency
+{%- when "check" -%}This is an informational check
+{%- else -%}This is a conformance test
+{%- endcase -%}
+{%- if test.browser_only %} run on browsers only{% endif -%}
+{%- if test.cdn_only %} run on CDN caches only{% endif -%}
+{%- if test.browser_skip %} not run on browsers{% endif -%}
+.
+
+{%- if test.depends_on %} It depends on the following test IDs:
+{%- for dependant in test.depends_on %}
+- `{{ dependant }}`
+{%- endfor %}{% endif %}
+
+{% for request in test.requests -%}
+## Request {{ forloop.index }}
+
+{%- if request.setup == true %}
+
+_This is a setup request; if it fails, we can't perform the test._
+{%- endif -%}
+
+{%- if request.disconnect == true %}
+
+The server will disconnect the client when receiving this request.
+{%- endif -%}
+
+
+{%- if request.mode or request.credentials or request.cache or request.redirect %}
+
+### Fetch [init](https://fetch.spec.whatwg.org/#requestinit):
+{%- if request.mode %}
+- Mode: {{ request.mode }}
+{%- endif -%}
+{%- if request.credentials %}
+- Credentials: {{ request.credentials }}
+{%- endif -%}
+{%- if request.cache %}
+- Cache: {{ request.cache }}
+{%- endif -%}
+{%- if request.redirect %}
+- Redirect: {{ request.redirect }}
+{%- endif -%}
+{%- endif %}
+
+### The client sends a request containing:
+~~~
+{{ request.request_method | default: 'GET' }} [generated test URL]{% if request.filename %}/{% endif %}{{ request.filename }}{% if request.query_arg %}?{% endif %}{{ request.query_arg }} {{ magic_locations }}
+{% for header in request.request_headers %}{% render 'header-magic' with header as header %}
+{% endfor %}
+{{ request.request_body }}
+~~~
+
+{%- if request.response_pause %}
+
+The server will pause for {{ request.response_pause }} seconds before responding.{% endif -%}
+
+{%- if request.response_status or request.response_headers or request.response_body %}
+
+### The server sends a response containing:
+~~~
+{% if request.expected_type == "lm_validated" or request.expected_type = "etag_validated" -%}
+HTTP/1.1 304 Not Modified
+{%- else -%}
+HTTP/1.1 {{ request.response_status[0] | default: 200 }} {{ request.response_status[1] | default: "OK" }}
+{%- endif %}
+{% for header in request.response_headers %}{% render 'header-magic' with header as header %}
+{% endfor %}
+{{ request.response_body | default: '[generated response body]' }}
+~~~{% endif -%}
+
+{%- if request.rfc850date %}
+All instances of the following headers will be send and checked for being in RFC850 date format:
+{% for header in request.rfc850date %}
+ - `{{ header }}`
+{% endfor %}
+{% endif -%}
+
+{% render 'checks' with request as request %}
+
+{%- if request.pause_after == true %}
+
+The client will pause for three seconds after this request.{% endif %}
+
+{% endfor %}
diff --git a/test/fixtures/cache-tests/test-engine/lib/tpl/header-list.liquid b/test/fixtures/cache-tests/test-engine/lib/tpl/header-list.liquid
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/tpl/header-list.liquid
@@ -0,0 +1,16 @@
+{%- for header in headers %}
+  - {% if header.first -%}
+      {%- if header.size == 3 -%}
+        {%- case header[1] -%}
+          {%- when ">" -%}
+            `{{ header[0] }}` is greater than `{{ header[2] }}`
+          {%- when "=" -%}
+            `{{ header[0] }}` has the same value as `{{ header[2] }}`
+        {%- endcase -%}
+      {%- else -%}
+        `{% render 'header-magic' with header as header %}`
+      {%- endif -%}
+    {%- else -%}
+      `{{ header }}`
+    {%- endif -%}
+{% endfor -%}
diff --git a/test/fixtures/cache-tests/test-engine/lib/tpl/header-magic.liquid b/test/fixtures/cache-tests/test-engine/lib/tpl/header-magic.liquid
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/tpl/header-magic.liquid
@@ -0,0 +1,6 @@
+{%- assign mytype = header[1] | typeof -%}
+{{ header[0] }}: {% if mytype == 'number' -%}
+  [ {{ header[1] | toLocaleString }} seconds delta ]
+{%- else -%}
+  {{ header[1] }}
+{%- endif -%}
diff --git a/test/fixtures/cache-tests/test-engine/lib/utils.mjs b/test/fixtures/cache-tests/test-engine/lib/utils.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/lib/utils.mjs
@@ -0,0 +1,89 @@
+export function AssertionError (options) {
+  this.name = 'Assertion'
+  this.message = options.message
+}
+
+export function SetupError (options) {
+  this.name = 'Setup'
+  this.message = options.message
+}
+
+export function assert (isSetup, expr, message) {
+  if (expr) return
+  if (isSetup) {
+    throw new SetupError({ message })
+  } else {
+    throw new AssertionError({ message })
+  }
+}
+
+export function token () {
+  return [toHex(randInt(32), 8),
+    toHex(randInt(16), 4),
+    toHex(0x4000 | randInt(12), 4),
+    toHex(0x8000 | randInt(14), 4),
+    toHex(randInt(48), 12)].join('-')
+}
+
+function randInt (bits) {
+  if (bits < 1 || bits > 53) {
+    throw new TypeError()
+  } else {
+    if (bits >= 1 && bits <= 30) {
+      return 0 | ((1 << bits) * Math.random())
+    } else {
+      const high = (0 | ((1 << (bits - 30)) * Math.random())) * (1 << 30)
+      const low = 0 | ((1 << 30) * Math.random())
+      return high + low
+    }
+  }
+}
+
+function toHex (x, length) {
+  let rv = x.toString(16)
+  while (rv.length < length) {
+    rv = '0' + rv
+  }
+  return rv
+}
+
+const rfc850day = {
+  0: 'Sunday',
+  1: 'Monday',
+  2: 'Tuesday',
+  3: 'Wednesday',
+  4: 'Thursday',
+  5: 'Friday',
+  6: 'Saturday'
+}
+
+const rfc850month = {
+  0: 'Jan',
+  1: 'Feb',
+  2: 'Mar',
+  3: 'Apr',
+  4: 'May',
+  5: 'Jun',
+  6: 'Jul',
+  7: 'Aug',
+  8: 'Sep',
+  9: 'Oct',
+  10: 'Nov',
+  11: 'Dec'
+}
+
+export function httpDate (now, deltaSecs, format) {
+  const instant = new Date(now + (deltaSecs * 1000))
+  if (format && format === 'rfc850') {
+    const day = rfc850day[instant.getUTCDay()]
+    const date = instant.getUTCDate().toString().padStart(2, '0')
+    const month = rfc850month[instant.getUTCMonth()]
+    const year = instant.getUTCFullYear().toString().slice(2)
+    const hours = instant.getUTCHours().toString().padStart(2, '0')
+    const mins = instant.getUTCMinutes().toString().padStart(2, '0')
+    const secs = instant.getUTCSeconds().toString().padStart(2, '0')
+    // Sunday, 06-Nov-94 08:49:37 GMT
+    return `${day}, ${date}-${month}-${year} ${hours}:${mins}:${secs} GMT`
+  }
+  return instant.toGMTString()
+}
diff --git a/test/fixtures/cache-tests/test-engine/server/handle-config.mjs b/test/fixtures/cache-tests/test-engine/server/handle-config.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/server/handle-config.mjs
@@ -0,0 +1,22 @@
+import { sendResponse, configs, setConfig } from './utils.mjs'
+
+export default function handleConfig (pathSegs, request, response) {
+  const uuid = pathSegs[0]
+  if (request.method !== 'PUT') {
+    sendResponse(response, 405, `${request.method} request to config for ${uuid}`)
+    return
+  }
+  if (configs.has(uuid)) {
+    sendResponse(response, 409, `Config already exists for ${uuid}`)
+    return
+  }
+  let body = ''
+  request.on('data', chunk => {
+    body += chunk
+  })
+  request.on('end', () => {
+    setConfig(uuid, JSON.parse(body))
+    response.statusCode = 201
+    response.end('OK')
+  })
+}
diff --git a/test/fixtures/cache-tests/test-engine/server/handle-file.mjs b/test/fixtures/cache-tests/test-engine/server/handle-file.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/server/handle-file.mjs
@@ -0,0 +1,24 @@
+import fs from 'fs'
+import path from 'path'
+import process from 'process'
+
+import { sendResponse } from './utils.mjs'
+import { mimeTypes } from '../lib/defines.mjs'
+
+export default function handleFile (url, request, response) {
+  let urlPath = path.normalize(url.pathname)
+  if (urlPath === '/') urlPath = '/index.html'
+  const filename = path.join(process.cwd(), urlPath)
+  let stat
+  try {
+    stat = fs.statSync(filename)
+  } catch {}
+  if (!stat || !stat.isFile()) {
+    sendResponse(response, 404, `${urlPath} Not Found`)
+    return
+  }
+  const mimeType = mimeTypes[path.extname(filename).split('.')[1]] || 'application/octet-stream'
+  const fileStream = fs.createReadStream(filename)
+  response.writeHead(200, { 'Content-Type': mimeType })
+  fileStream.pipe(response)
+}
diff --git a/test/fixtures/cache-tests/test-engine/server/handle-state.mjs b/test/fixtures/cache-tests/test-engine/server/handle-state.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/server/handle-state.mjs
@@ -0,0 +1,12 @@
+import { sendResponse, stash } from './utils.mjs'
+
+export default function handleState (pathSegs, request, response) {
+  const uuid = pathSegs[0]
+  const state = stash.get(uuid)
+  if (state === undefined) {
+    sendResponse(response, 404, `State not found for ${uuid}`)
+    return
+  }
+  response.setHeader('Content-Type', 'text/plain')
+  response.end(JSON.stringify(state))
+}
diff --git a/test/fixtures/cache-tests/test-engine/server/handle-test.mjs b/test/fixtures/cache-tests/test-engine/server/handle-test.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/server/handle-test.mjs
@@ -0,0 +1,118 @@
+import { noBodyStatus } from '../lib/defines.mjs'
+import { fixupHeader } from '../lib/header-fixup.mjs'
+import { sendResponse, getHeader, configs, stash, setStash, logRequest, logResponse } from './utils.mjs'
+
+export default function handleTest (pathSegs, request, response) {
+  // identify the desired configuration for this request
+  const uuid = pathSegs[0]
+  if (!uuid) {
+    sendResponse(response, 404, `Config Not Found for ${uuid}`)
+    return
+  }
+  const requests = configs.get(uuid)
+  if (!requests) {
+    sendResponse(response, 409, `Requests not found for ${uuid}`)
+    return
+  }
+
+  const serverState = stash.get(uuid) || []
+  const srvReqNum = serverState.length + 1
+  const cliReqNum = parseInt(request.headers['req-num'])
+  const reqNum = cliReqNum || srvReqNum
+  const reqConfig = requests[reqNum - 1]
+
+  if (!reqConfig) {
+    sendResponse(response, 409, `${requests[0].id} config not found for request ${srvReqNum} (anticipating ${requests.length})`)
+    return
+  }
+  if (reqConfig.dump) logRequest(request, srvReqNum)
+
+  // response_pause
+  if ('response_pause' in reqConfig) {
+    setTimeout(continueHandleTest, reqConfig.response_pause * 1000, uuid, request, response, requests, serverState)
+  } else {
+    continueHandleTest(uuid, request, response, requests, serverState)
+  }
+}
+
+function continueHandleTest (uuid, request, response, requests, serverState) {
+  const srvReqNum = serverState.length + 1
+  const cliReqNum = parseInt(request.headers['req-num'])
+  const reqNum = cliReqNum || srvReqNum
+  const reqConfig = requests[reqNum - 1]
+  const previousConfig = requests[reqNum - 2]
+  const now = Date.now()
+
+  // Determine what the response status should be
+  let httpStatus = reqConfig.response_status || [200, 'OK']
+  if ('expected_type' in reqConfig && reqConfig.expected_type.endsWith('validated')) {
+    const previousLm = getHeader(previousConfig.response_headers, 'Last-Modified')
+    if (previousLm && request.headers['if-modified-since'] === previousLm) {
+      httpStatus = [304, 'Not Modified']
+    }
+    const previousEtag = getHeader(previousConfig.response_headers, 'ETag')
+    if (previousEtag && request.headers['if-none-match'] === previousEtag) {
+      httpStatus = [304, 'Not Modified']
+    }
+    if (httpStatus[0] !== 304) {
+      httpStatus = [999, '304 Not Generated']
+    }
+  }
+  response.statusCode = httpStatus[0]
+  response.statusPhrase = httpStatus[1]
+
+  // header manipulation
+  const responseHeaders = reqConfig.response_headers || []
+  const savedHeaders = new Map()
+  response.setHeader('Server-Base-Url', request.url)
+  response.setHeader('Server-Request-Count', srvReqNum)
+  response.setHeader('Client-Request-Count', cliReqNum)
+  response.setHeader('Server-Now', now, 0)
+  responseHeaders.forEach(header => {
+    header = fixupHeader(header, response.getHeaders(), reqConfig)
+    if (response.hasHeader(header[0])) {
+      const currentVal = response.getHeader(header[0])
+      if (typeof currentVal === 'string') {
+        response.setHeader(header[0], [currentVal, header[1]])
+      } else if (Array.isArray(currentVal)) {
+        response.setHeader(header[0], currentVal.concat(header[1]))
+      } else {
+        console.log(`ERROR: Unanticipated header type of ${typeof currentVal} for ${header[0]}`)
+      }
+    } else {
+      response.setHeader(header[0], header[1])
+    }
+    if (header.length < 3 || header[2] === true) {
+      savedHeaders.set(header[0], response.getHeader(header[0]))
+    }
+  })
+
+  if (!response.hasHeader('content-type')) {
+    response.setHeader('Content-Type', 'text/plain')
+  }
+
+  // stash information about this request for the client
+  serverState.push({
+    request_num: cliReqNum,
+    request_method: request.method,
+    request_headers: request.headers,
+    response_headers: Array.from(savedHeaders.entries())
+  })
+  response.setHeader('Request-Numbers', serverState.map(item => item.request_num).join(' '))
+  setStash(uuid, serverState)
+
+  // Response body generation
+  if ('disconnect' in reqConfig && reqConfig.disconnect) {
+    // disconnect now because we want the state
+    response.socket.destroy()
+    response = 'disconnect'
+  } else if (noBodyStatus.has(response.statusCode)) {
+    response.end()
+  } else {
+    const content = reqConfig.response_body || uuid
+    response.end(content)
+  }
+
+  // logging
+  if (reqConfig.dump) logResponse(response, srvReqNum)
+}
diff --git a/test/fixtures/cache-tests/test-engine/server/server.mjs b/test/fixtures/cache-tests/test-engine/server/server.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/server/server.mjs
@@ -0,0 +1,54 @@
+/* global URL */
+
+import fs from 'fs'
+import http from 'http'
+import https from 'https'
+import process from 'process'
+
+import handleConfig from './handle-config.mjs'
+import handleFile from './handle-file.mjs'
+import handleState from './handle-state.mjs'
+import handleTest from './handle-test.mjs'
+
+function handleMain (request, response) {
+  const url = new URL(request.url, baseUrl)
+  const pathSegs = url.pathname.split('/')
+  pathSegs.shift()
+  const dispatch = pathSegs.shift()
+  if (dispatch === 'config') {
+    handleConfig(pathSegs, request, response)
+  } else if (dispatch === 'test') {
+    handleTest(pathSegs, request, response)
+  } else if (dispatch === 'state') {
+    handleState(pathSegs, request, response)
+  } else {
+    handleFile(url, request, response)
+  }
+}
+
+const protocol = process.env.npm_config_protocol || process.env.npm_package_config_protocol
+const port = process.env.npm_config_port || process.env.npm_package_config_port
+const baseUrl = `${protocol}://localhost:${port}/`
+const pidfile = process.env.npm_config_pidfile || process.env.npm_package_config_pidfile
+
+fs.writeFile(pidfile, process.pid.toString(), 'ascii', function (err) {
+  if (err) { console.log(`PID file write error: ${err.message}`) }
+})
+
+let server
+if (protocol.toLowerCase() === 'https') {
+  const options = {
+    key: fs.readFileSync(process.env.npm_config_keyfile),
+    cert: fs.readFileSync(process.env.npm_config_certfile)
+  }
+  server = https.createServer(options, handleMain)
+} else {
+  server = http.createServer(handleMain)
+}
+server.on('listening', () => {
+  const host = (server.address().family === 'IPv6')
+    ? `[${server.address().address}]`
+    : server.address().address
+  console.log(`Listening on ${protocol.toLowerCase()}://${host}:${server.address().port}/`)
+})
+server.listen(port)
diff --git a/test/fixtures/cache-tests/test-engine/server/utils.mjs b/test/fixtures/cache-tests/test-engine/server/utils.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/test-engine/server/utils.mjs
@@ -0,0 +1,54 @@
+import { BLUE, NC } from '../lib/defines.mjs'
+
+export function sendResponse (response, statusCode, message) {
+  console.log(`SERVER WARNING: ${message}`)
+  response.writeHead(statusCode, { 'Content-Type': 'text/plain' })
+  response.write(`${message}\n`)
+  response.end()
+}
+
+export function getHeader (headers, headerName) {
+  let result
+  headers.forEach(header => {
+    if (header[0].toLowerCase() === headerName.toLowerCase()) {
+      result = header[1]
+    }
+  })
+  return result
+}
+
+// stash for server state
+export const stash = new Map()
+
+export function setStash (key, value) {
+  stash.set(key, value)
+}
+
+// configurations
+export const configs = new Map()
+
+export function setConfig (key, value) {
+  configs.set(key, value)
+}
+
+export function logRequest (request, reqNum) {
+  console.log(`${BLUE}=== Server request ${reqNum}${NC}`)
+  console.log(`    ${request.method} ${request.url}`)
+  for (const [key, value] of Object.entries(request.headers)) {
+    console.log(`    ${key}: ${value}`)
+  }
+  console.log('')
+}
+
+export function logResponse (response, resNum) {
+  console.log(`${BLUE}=== Server response ${resNum}${NC}`)
+  if (response === 'disconnect') {
+    console.log('    [ server disconnect ]')
+  } else {
+    console.log(`    HTTP ${response.statusCode} ${response.statusPhrase}`)
+    for (const [key, value] of Object.entries(response.getHeaders())) {
+      console.log(`    ${key}: ${value}`)
+    }
+  }
+  console.log('')
+}
diff --git a/test/fixtures/cache-tests/test-host.sh b/test/fixtures/cache-tests/test-host.sh
new file mode 100755
--- /dev/null
+++ b/test/fixtures/cache-tests/test-host.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+## Run tests against a host/port combination.
+
+set -euo pipefail
+
+function usage {
+  if [[ -n "${1}" ]]; then
+    echo "${1}"
+  fi
+  echo "Usage: ${0} [ -i test-id ] host[:port]"
+}
+
+function run {
+  TEST_ID="$1"
+  HOST="$2"
+  if [[ -z $TEST_ID ]]; then
+    npm run --silent cli --base="http://${HOST}"
+  else
+    npm run --silent cli --base="http://${HOST}" --id="${TEST_ID}"
+  fi
+
+}
+
+TEST_ID=""
+while getopts "h?i:" opt; do
+  case "${opt}" in
+    h)
+      usage
+      exit 0
+      ;;
+    i)
+      TEST_ID=$OPTARG
+      ;;
+    *)
+      usage
+      exit 1
+      ;;
+  esac
+done
+shift $((OPTIND-1))
+
+if [[ $# -ne 1 ]]; then
+  usage "Please specify a host:port."
+  exit 1
+fi
+
+run "$TEST_ID" "$1"
diff --git a/test/fixtures/cache-tests/tests/age-parse.mjs b/test/fixtures/cache-tests/tests/age-parse.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/age-parse.mjs
@@ -0,0 +1,301 @@
+export default
+
+{
+  name: 'Age Parsing',
+  id: 'age-parse',
+  description: 'These tests check how caches parse the `Age` response header.',
+  spec_anchors: ['field.age', 'expiration.model'],
+  tests: [
+    {
+      name: 'HTTP cache should ignore an `Age` header with a non-numeric value',
+      id: 'age-parse-nonnumeric',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', 'abc', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should ignore an `Age` header with a negative value',
+      id: 'age-parse-negative',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '-7200', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should ignore an `Age` header with a float value',
+      id: 'age-parse-float',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '7200.0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with a `Age` value of 2147483647 to be stale',
+      id: 'age-parse-large-minus-one',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '2147483647', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with a `Age` value of 2147483648 to be stale',
+      id: 'age-parse-large',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '2147483648', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with a `Age` value of 2147483649 to be stale',
+      id: 'age-parse-larger',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '2147483649', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with a single `Age` header line `old, 0` to be stale',
+      id: 'age-parse-suffix',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '7200, 0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with a single `Age` header line `0, old` to be fresh',
+      id: 'age-parse-prefix',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '0, 7200', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should use the first line in a response with multiple `Age` header lines: `old`, `0`',
+      id: 'age-parse-suffix-twoline',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '7200', false],
+            ['Age', '0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should use the first line in a response with multiple `Age` header lines: `0`, `old`',
+      id: 'age-parse-prefix-twoline',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '0', false],
+            ['Age', '7200', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with a single line `Age: 0, 0` to be fresh',
+      id: 'age-parse-dup-0',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '0, 0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with two `Age: 0` header lines to be fresh',
+      id: 'age-parse-dup-0-twoline',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '0', false],
+            ['Age', '0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache should consider a response with two `Age: not_old` header lines to be fresh',
+      id: 'age-parse-dup-old',
+      depends_on: ['freshness-max-age-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=10000'],
+            ['Age', '3600', false],
+            ['Age', '3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache consider an alphabetic parameter on `Age` header to be valid?',
+      id: 'age-parse-parameter',
+      depends_on: ['freshness-max-age-age'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '7200;foo=bar', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache should consider a numeric parameter on `Age` header to be valid?',
+      id: 'age-parse-numeric-parameter',
+      depends_on: ['freshness-max-age-age'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '7200;foo=111', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/authorization.mjs b/test/fixtures/cache-tests/tests/authorization.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/authorization.mjs
@@ -0,0 +1,110 @@
+import * as templates from './lib/templates.mjs'
+
+export default
+
+{
+  name: 'Storing Respones to Authenticated Requests',
+  id: 'auth',
+  description: 'These tests check for behaviours regarding authenticated HTTP responses.',
+  spec_anchors: ['caching.authenticated.responses'],
+  tests: [
+    {
+      name: 'HTTP shared cache must not reuse a response to a request that contained `Authorization`, even with explicit freshness',
+      id: 'other-authorization',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          expected_request_headers: [
+            ['Authorization', 'FOO']
+          ]
+        }),
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP shared cache reuses a response to a request that contained `Authorization`, if it has `Cache-Control: public`',
+      id: 'other-authorization-public',
+      kind: 'optimal',
+      browser_skip: true,
+      depends_on: ['other-authorization'],
+      spec_anchors: ['cache-response-directive.public'],
+      requests: [
+        {
+          request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          expected_request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600, public'],
+            ['Date', 0]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP shared cache reuses a response to a request that contained `Authorization`, if it has `Cache-Control: must-revalidate`',
+      id: 'other-authorization-must-revalidate',
+      kind: 'optimal',
+      browser_skip: true,
+      depends_on: ['other-authorization'],
+      requests: [
+        {
+          request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          expected_request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600, must-revalidate'],
+            ['Date', 0]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP shared cache reuses a response to a request that contained `Authorization`, if it has `Cache-Control: s-maxage`',
+      id: 'other-authorization-smaxage',
+      kind: 'optimal',
+      browser_skip: true,
+      depends_on: ['other-authorization'],
+      requests: [
+        {
+          request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          expected_request_headers: [
+            ['Authorization', 'FOO']
+          ],
+          response_headers: [
+            ['Cache-Control', 's-maxage=3600'],
+            ['Date', 0]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/cc-freshness.mjs b/test/fixtures/cache-tests/tests/cc-freshness.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/cc-freshness.mjs
@@ -0,0 +1,468 @@
+import * as templates from './lib/templates.mjs'
+
+export default
+
+{
+  name: 'Cache-Control Freshness',
+  id: 'cc-freshness',
+  description: 'These tests check how caches calculate freshness using `Cache-Control`.',
+  spec_anchors: ['expiration.model', 'cache-response-directive'],
+  tests: [
+    {
+      name: 'Does HTTP cache avoid reusing a response without explict freshness information or a validator (reuse is allowed, but not common, and many tests rely upon a cache _not_ doing it)?',
+      id: 'freshness-none',
+      kind: 'check',
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with positive `Cache-Control: max-age`',
+      id: 'freshness-max-age',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with `Cache-Control: max-age` after it becomes stale',
+      id: 'freshness-max-age-stale',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with `Cache-Control: max-age=0`',
+      id: 'freshness-max-age-0',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=0']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with `Cache-Control: max-age: 2147483647`',
+      id: 'freshness-max-age-max-minus-1',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2147483647']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with `Cache-Control: max-age: 2147483648`',
+      id: 'freshness-max-age-max',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2147483648']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with `Cache-Control: max-age: 2147483649`',
+      id: 'freshness-max-age-max-plus-1',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2147483649']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with `Cache-Control: max-age: 99999999999`',
+      id: 'freshness-max-age-max-plus',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=99999999999']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response when the `Age` header is greater than its `Cache-Control: max-age` freshness lifetime',
+      id: 'freshness-max-age-age',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-response-directive.max-age', 'field.age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Cache-Control', 'max-age=3600'],
+            ['Age', '7200']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache consider `Date` when applying `Cache-Control: max-age` (i.e., is `apparent_age` used)?',
+      id: 'freshness-max-age-date',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Date', -7200],
+            ['Cache-Control', 'max-age=3600']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with positive `Cache-Control: max-age` and a past `Expires`',
+      id: 'freshness-max-age-expires',
+      depends_on: ['freshness-max-age'],
+      kind: 'optimal',
+      spec_anchors: ['cache-response-directive.max-age', 'field.expires'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Expires', -7200],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with positive `Cache-Control: max-age` and an invalid `Expires`',
+      id: 'freshness-max-age-expires-invalid',
+      depends_on: ['freshness-max-age'],
+      kind: 'optimal',
+      spec_anchors: ['cache-response-directive.max-age', 'field.expires'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Expires', '0', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with `Cache-Control: max-age=0` and a future `Expires`',
+      id: 'freshness-max-age-0-expires',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age', 'field.expires'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 3600],
+            ['Cache-Control', 'max-age=0'],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with positive `Cache-Control: max-age` and a CC extension present',
+      id: 'freshness-max-age-extension',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache.control.extensions'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'foobar, max-age=3600']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with positive `Cache-Control: MaX-AgE`',
+      id: 'freshness-max-age-case-insenstive',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'MaX-aGe=3600']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with negative `Cache-Control: max-age`',
+      id: 'freshness-max-age-negative',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=-3600']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Private HTTP cache must not prefer `Cache-Control: s-maxage` over shorter `Cache-Control: max-age`',
+      id: 'freshness-max-age-s-maxage-private',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 's-maxage=3600, max-age=1']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ],
+      browser_only: true
+    },
+    {
+      name: 'Private HTTP cache must not prefer `Cache-Control: s-maxage` over shorter `Cache-Control: max-age` (multiple headers)',
+      id: 'freshness-max-age-s-maxage-private-multiple',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 's-maxage=3600'],
+            ['Cache-Control', 'max-age=1']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ],
+      browser_only: true
+    },
+    {
+      name: 'An optimal shared HTTP cache reuses a response with positive `Cache-Control: s-maxage`',
+      id: 'freshness-s-maxage-shared',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 's-maxage=3600']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ],
+      browser_skip: true
+    },
+    {
+      name: 'Shared HTTP cache must prefer short `Cache-Control: s-maxage` over a longer `Cache-Control: max-age`',
+      id: 'freshness-max-age-s-maxage-shared-longer',
+      depends_on: ['freshness-s-maxage-shared'],
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600, s-maxage=1']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ],
+      browser_skip: true
+    },
+    {
+      name: 'Shared HTTP cache must prefer short `Cache-Control: s-maxage` over a longer `Cache-Control: max-age` (reversed)',
+      id: 'freshness-max-age-s-maxage-shared-longer-reversed',
+      depends_on: ['freshness-s-maxage-shared'],
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 's-maxage=1, max-age=3600']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ],
+      browser_skip: true
+    },
+    {
+      name: 'Shared HTTP cache must prefer short `Cache-Control: s-maxage` over a longer `Cache-Control: max-age` (multiple headers)',
+      id: 'freshness-max-age-s-maxage-shared-longer-multiple',
+      depends_on: ['freshness-s-maxage-shared'],
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Cache-Control', 's-maxage=1']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ],
+      browser_skip: true
+    },
+    {
+      name: 'An optimal shared HTTP cache prefers long `Cache-Control: s-maxage` over a shorter `Cache-Control: max-age`',
+      id: 'freshness-max-age-s-maxage-shared-shorter',
+      depends_on: ['freshness-s-maxage-shared'],
+      kind: 'optimal',
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1, s-maxage=3600']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ],
+      browser_skip: true
+    },
+    {
+      name: 'An optimal shared HTTP cache prefers long `Cache-Control: s-maxage` over `Cache-Control: max-age=0`, even with a past `Expires`',
+      id: 'freshness-max-age-s-maxage-shared-shorter-expires',
+      depends_on: ['freshness-s-maxage-shared'],
+      kind: 'optimal',
+      spec_anchors: ['cache-response-directive.max-age', 'cache-response-directive.s-maxage'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', -10],
+            ['Cache-Control', 'max-age=0, s-maxage=3600']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ],
+      browser_skip: true
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/cc-parse.mjs b/test/fixtures/cache-tests/tests/cc-parse.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/cc-parse.mjs
@@ -0,0 +1,278 @@
+export default
+
+{
+  name: 'Cache-Control Parsing',
+  id: 'cc-parse',
+  description: 'These tests check how caches parse the `Cache-Control` response header.',
+  spec_anchors: ['field.cache-control'],
+  tests: [
+    {
+      name: 'Does HTTP cache reuse a response when first `Cache-Control: max-age` is fresh, but second is stale (same line)?',
+      id: 'freshness-max-age-two-fresh-stale-sameline',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1800, max-age=1', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a response when first `Cache-Control: max-age` is fresh, but second is stale (separate lines)?',
+      id: 'freshness-max-age-two-fresh-stale-sepline',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1800', false],
+            ['Cache-Control', 'max-age=1', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a response when first `Cache-Control: max-age` is stale, but second is fresh (same line)?',
+      id: 'freshness-max-age-two-stale-fresh-sameline',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1, max-age=1800', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a response when first `Cache-Control: max-age` is stale, but second is fresh (separate lines)?',
+      id: 'freshness-max-age-two-stale-fresh-sepline',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1', false],
+            ['Cache-Control', 'max-age=1800', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a response with a quoted `Cache-Control: max-age`?',
+      id: 'freshness-max-age-quoted',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age="3600"', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with `max-age` in a quoted string (before the "real" `max-age`)',
+      id: 'freshness-max-age-ignore-quoted',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'extension="max-age=3600", max-age=1', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache mut not reuse a response with `max-age` in a quoted string (after the "real" `max-age`)',
+      id: 'freshness-max-age-ignore-quoted-rev',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1, extension="max-age=3600"', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache ignore max-age with space before the `=`?',
+      id: 'freshness-max-age-space-before-equals',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age =3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache ignore max-age with space after the `=`?',
+      id: 'freshness-max-age-space-after-equals',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age= 3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses max-age with the value `003600`',
+      id: 'freshness-max-age-leading-zero',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=003600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with a single-quoted `Cache-Control: max-age`',
+      id: 'freshness-max-age-single-quoted',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=\'3600\'', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse max-age with `3600.0` value?',
+      id: 'freshness-max-age-decimal-zero',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600.0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse max-age with `3600.5` value?',
+      id: 'freshness-max-age-decimal-five',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600.5', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a response with an invalid `Cache-Control: max-age` (leading alpha)?',
+      id: 'freshness-max-age-a100',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=a3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a response with an invalid `Cache-Control: max-age` (trailing alpha)?',
+      id: 'freshness-max-age-100a',
+      kind: 'check',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600a', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/cc-request.mjs b/test/fixtures/cache-tests/tests/cc-request.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/cc-request.mjs
@@ -0,0 +1,241 @@
+import * as templates from './lib/templates.mjs'
+import * as utils from './lib/utils.mjs'
+
+export default {
+  name: 'Cache-Control Request Directives',
+  id: 'cc-request',
+  description: 'These tests check to see if caches respect `Cache-Control` request directives. Note that HTTP does not require them to be supported.',
+  spec_anchors: ['cache-request-directive'],
+  tests: [
+    {
+      name: 'Does HTTP cache honor request `Cache-Control: max-age=0` when it holds a fresh response?',
+      id: 'ccreq-ma0',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.max-age'],
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['Cache-Control', 'max-age=0']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: max-age=1` when it holds a fresh response?',
+      id: 'ccreq-ma1',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.max-age'],
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['Cache-Control', 'max-age=1']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: max-age` when it holds a fresh but `Age`d response that is not fresh enough?',
+      id: 'ccreq-magreaterage',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.max-age'],
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Age', '1800']
+          ]
+        }),
+        {
+          request_headers: [
+            ['Cache-Control', 'max-age=600']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a stale response when request `Cache-Control: max-stale` allows it?',
+      id: 'ccreq-max-stale',
+      kind: 'check',
+      depends_on: ['freshness-max-age-stale'],
+      spec_anchors: ['cache-request-directive.max-stale'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          request_headers: [
+            ['Cache-Control', 'max-stale=1000']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a stale `Age`d response when request `Cache-Control: max-stale` allows it?',
+      id: 'ccreq-max-stale-age',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.max-stale'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1500'],
+            ['Age', '2000']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Cache-Control', 'max-stale=1000']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: min-fresh` when the response it holds is not fresh enough?',
+      id: 'ccreq-min-fresh',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.min-fresh'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1500']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Cache-Control', 'min-fresh=2000']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: min-fresh` when the `Age`d response it holds is not fresh enough?',
+      id: 'ccreq-min-fresh-age',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.min-fresh'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1500'],
+            ['Age', '1000']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Cache-Control', 'min-fresh=1000']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: no-cache` when it holds a fresh response?',
+      id: 'ccreq-no-cache',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Cache-Control', 'no-cache']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: no-cache` by validating a response with `Last-Modified`?',
+      id: 'ccreq-no-cache-lm',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Last-Modified', -10000],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Cache-Control', 'no-cache']
+          ],
+          expected_type: 'lm_validated'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: no-cache` by validating a response with an `ETag`?',
+      id: 'ccreq-no-cache-etag',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['ETag', utils.httpContent('abc')]
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Cache-Control', 'no-cache']
+          ],
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: no-store` when it holds a fresh response?',
+      id: 'ccreq-no-store',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['cache-request-directive.no-store'],
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['Cache-Control', 'no-store']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache honour request `Cache-Control: only-if-cached` by generating a `504` response when it does not have a stored response?',
+      id: 'ccreq-oic',
+      kind: 'check',
+      spec_anchors: ['cache-request-directive.only-if-cached'],
+      requests: [
+        {
+          request_headers: [
+            ['Cache-Control', 'only-if-cached']
+          ],
+          expected_status: 504,
+          expected_response_text: null
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/cc-response.mjs b/test/fixtures/cache-tests/tests/cc-response.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/cc-response.mjs
@@ -0,0 +1,375 @@
+export default
+
+{
+  name: 'Cache-Control Response Directives',
+  id: 'cc-response',
+  description: 'These tests check how caches handle response `Cache-Control` directives other than those related to freshness, like `no-cache` and `no-store`.',
+  spec_anchors: ['cache-response-directive'],
+  tests: [
+    {
+      name: 'Shared HTTP cache must not store a response with `Cache-Control: private`',
+      id: 'cc-resp-private-shared',
+      browser_skip: true,
+      spec_anchors: ['cache-response-directive.private'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'private, max-age=3600']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal private HTTP cache reuses a fresh response with `Cache-Control: private`',
+      id: 'cc-resp-private-private',
+      browser_only: true,
+      kind: 'optimal',
+      spec_anchors: ['cache-response-directive.private'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'private, max-age=3600']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not store a response with `Cache-Control: no-store`',
+      id: 'cc-resp-no-store',
+      spec_anchors: ['cache-response-directive.no-store'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'no-store']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not store a response with `Cache-Control: nO-StOrE`',
+      id: 'cc-resp-no-store-case-insensitive',
+      depends_on: ['cc-resp-no-store'],
+      spec_anchors: ['cache-response-directive.no-store'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'No-StOrE']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not store a response with `Cache-Control: no-store`, even with `max-age` and `Expires`',
+      id: 'cc-resp-no-store-fresh',
+      depends_on: ['cc-resp-no-store'],
+      spec_anchors: ['cache-response-directive.no-store'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000, no-store'],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache use older stored response when newer one came with `Cache-Control: no-store`?',
+      id: 'cc-resp-no-store-old-new',
+      depends_on: ['cc-resp-no-store'],
+      spec_anchors: ['cache-response-directive.no-store'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000'],
+            ['Expires', 10000],
+            ['Date', 0],
+            ['A', '1']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          response_headers: [
+            ['Cache-Control', 'no-store'],
+            ['Date', 0],
+            ['A', '2']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [['a', '1']]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache use older stored response when newer one came with `Cache-Control: no-store, max-age=0`?',
+      id: 'cc-resp-no-store-old-max-age',
+      depends_on: ['cc-resp-no-store'],
+      spec_anchors: ['cache-response-directive.no-store'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000'],
+            ['Expires', 10000],
+            ['Date', 0],
+            ['A', '1']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          response_headers: [
+            ['Cache-Control', 'no-store, max-age=0'],
+            ['Date', 0],
+            ['A', '2']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [['a', '1']]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not use a cached response with `Cache-Control: no-cache`, even with `max-age` and `Expires`',
+      id: 'cc-resp-no-cache',
+      spec_anchors: ['cache-response-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000, no-cache'],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not use a cached response with `Cache-Control: No-CaChE`, even with `max-age` and `Expires`',
+      id: 'cc-resp-no-cache-case-insensitive',
+      depends_on: ['cc-resp-no-cache'],
+      spec_anchors: ['cache-response-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000, No-CaChE'],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores a response with `Cache-Control: no-cache`, but revalidates it upon use',
+      id: 'cc-resp-no-cache-revalidate',
+      kind: 'optimal',
+      depends_on: ['cc-resp-no-cache'],
+      spec_anchors: ['cache-response-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'no-cache'],
+            ['ETag', '"abcd"']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores a response with `Cache-Control: no-cache`, but revalidates it upon use, even with `max-age` and `Expires`',
+      id: 'cc-resp-no-cache-revalidate-fresh',
+      kind: 'optimal',
+      depends_on: ['cc-resp-no-cache'],
+      spec_anchors: ['cache-response-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000, no-cache'],
+            ['Expires', 10000],
+            ['Date', 0],
+            ['ETag', '"abcd"']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'Does `Cache-Control: no-cache` inhibit storing a listed header?',
+      id: 'headers-omit-headers-listed-in-Cache-Control-no-cache-single',
+      kind: 'check',
+      depends_on: ['cc-resp-no-cache-revalidate'],
+      spec_anchors: ['cache-response-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'no-cache="a"'],
+            ['a', '1'],
+            ['b', '2'],
+            ['Cache-Control', 'max-age=3600'],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [['b', '2']],
+          expected_response_headers_missing: ['a'],
+          setup_tests: ['expected_type']
+        }
+      ]
+    },
+    {
+      name: 'Does `Cache-Control: no-cache` inhibit storing multiple listed headers?',
+      id: 'headers-omit-headers-listed-in-Cache-Control-no-cache',
+      kind: 'check',
+      depends_on: ['cc-resp-no-cache-revalidate'],
+      spec_anchors: ['cache-response-directive.no-cache'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'no-cache="a, b"'],
+            ['a', '1'],
+            ['b', '2'],
+            ['c', '3'],
+            ['Cache-Control', 'max-age=3600'],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [['c', '3']],
+          expected_response_headers_missing: ['a', 'b'],
+          setup_tests: ['expected_type']
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with positive `Cache-Control: max-age, must-revalidate`',
+      id: 'cc-resp-must-revalidate-fresh',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.must-revalidate'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000, must-revalidate'],
+            ['ETag', '"abcd"']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must revalidate a stale response with positive `Cache-Control: max-age, must-revalidate`',
+      id: 'cc-resp-must-revalidate-stale',
+      depends_on: ['freshness-none'],
+      spec_anchors: ['cache-response-directive.must-revalidate'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2, must-revalidate'],
+            ['ETag', '"abcd"']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached',
+          setup: true,
+          pause_after: true,
+          response_headers: [
+            ['Cache-Control', 'max-age=2, must-revalidate'],
+            ['ETag', '"abcd"']
+          ]
+        },
+        {
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a fresh response with `Cache-Control: immutable` without revalidation.',
+      id: 'cc-resp-immutable-fresh',
+      kind: 'optimal',
+      browser_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000, immutable'],
+            ['ETag', '"abcd"']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          cache: 'no-cache',
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'A HTTP cache MUST revalidate a stale response with `Cache-Control: immutable`',
+      id: 'cc-resp-immutable-stale',
+      browser_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2, immutable'],
+            ['ETag', '"abcd"']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          cache: 'no-cache',
+          expected_type: 'etag_validated',
+          expected_request_headers: [['cache-control', 'max-age=0']]
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/cdn-cache-control.mjs b/test/fixtures/cache-tests/tests/cdn-cache-control.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/cdn-cache-control.mjs
@@ -0,0 +1,491 @@
+export default
+
+{
+  name: 'CDN-Cache-Control',
+  id: 'cdn-cache-control',
+  description: 'These tests check non-browser caches for behaviours around the [`CDN-Cache-Control` response header](https://httpwg.org/specs/rfc9213.html).',
+  tests: [
+    {
+      name: 'An optimal CDN reuses a response with positive `CDN-Cache-Control: max-age`',
+      id: 'cdn-max-age',
+      cdn_only: true,
+      depends_on: ['freshness-none'],
+      kind: 'optimal',
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal CDN reuses a response with `CDN-Cache-Control: max-age: 2147483648`',
+      id: 'cdn-max-age-max',
+      kind: 'optimal',
+      cdn_only: true,
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=2147483648', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal CDN reuses a response with `CDN-Cache-Control: max-age: 99999999999`',
+      id: 'cdn-max-age-max-plus',
+      kind: 'optimal',
+      cdn_only: true,
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=99999999999', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must not reuse a response when the `Age` header is greater than its `CDN-Cache-Control: max-age` freshness lifetime',
+      id: 'cdn-max-age-age',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['CDN-Cache-Control', 'max-age=3600', false],
+            ['Age', '7200']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does CDN ignore `CDN-Cache-Control: max-age` with space before the `=`?',
+      id: 'cdn-max-age-space-before-equals',
+      cdn_only: true,
+      kind: 'check',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1'],
+            ['CDN-Cache-Control', 'max-age =100', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does CDN ignore `CDN-Cache-Control: max-age` with space after the `=`?',
+      id: 'cdn-max-age-space-after-equals',
+      cdn_only: true,
+      kind: 'check',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1'],
+            ['CDN-Cache-Control', 'max-age= 100', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must not reuse a response with `CDN-Cache-Control: max-age=0`',
+      id: 'cdn-max-age-0',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=0', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal CDN reuses a response with a positive `CDN-Cache-Control: max-age` and an extension cache directive',
+      id: 'cdn-max-age-extension',
+      cdn_only: true,
+      kind: 'optimal',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'foobar, max-age=3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does CDN reuse a response with a positive `CDN-Cache-Control: MaX-aGe`?',
+      id: 'cdn-max-age-case-insensitive',
+      cdn_only: true,
+      kind: 'check',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'MaX-aGe=3600', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+
+    {
+      name: 'An optimal CDN reuses a response with a positive `CDN-Cache-Control: max-age` and a past `Expires`',
+      id: 'cdn-max-age-expires',
+      cdn_only: true,
+      kind: 'optimal',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=3600', false],
+            ['Expires', -10000],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal CDN reuses a response with a positive `CDN-Cache-Control: max-age` and an invalid `Expires`',
+      id: 'cdn-max-age-cc-max-age-invalid-expires',
+      cdn_only: true,
+      kind: 'optimal',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=3600', false],
+            ['Expires', '0', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must not reuse a response with a `CDN-Cache-Control: max-age=0` and a future `Expires`',
+      id: 'cdn-max-age-0-expires',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=0', false],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal CDN prefers a long `CDN-Cache-Control: max-age` over a short `Cache-Control: max-age`',
+      id: 'cdn-max-age-short-cc-max-age',
+      cdn_only: true,
+      kind: 'optimal',
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1'],
+            ['CDN-Cache-Control', 'max-age=3600', false]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must prefer a short `CDN-Cache-Control: max-age` over a long `Cache-Control: max-age`',
+      id: 'cdn-max-age-long-cc-max-age',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['CDN-Cache-Control', 'max-age=1', false]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must not reuse a cached response with `CDN-Cache-Control: private`, even with `Cache-Control: max-age` and `Expires`',
+      id: 'cdn-private',
+      cdn_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'private'],
+            ['Cache-Control', 'max-age=10000'],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must not reuse a cached response with `CDN-Cache-Control: no-cache`, even with `Cache-Control: max-age` and `Expires`',
+      id: 'cdn-no-cache',
+      cdn_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'no-cache'],
+            ['Cache-Control', 'max-age=10000'],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN must not store a response with `CDN-Cache-Control: no-store`, even with `Cache-Control: max-age` and `Expires`',
+      id: 'cdn-no-store-cc-fresh',
+      cdn_only: true,
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=10000'],
+            ['CDN-Cache-Control', 'no-store', false],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal CDN stores a response with a fresh `CDN-Cache-Control: max-age`, even with `Cache-Control: no-store`',
+      id: 'cdn-fresh-cc-nostore',
+      depends_on: ['freshness-none'],
+      cdn_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'no-store'],
+            ['CDN-Cache-Control', 'max-age=10000', false]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN should ignore a `CDN-Cache-Control` that\'s an invalid Structured Field (unknown type)',
+      id: 'cdn-cc-invalid-sh-type-unknown',
+      depends_on: ['cdn-max-age'],
+      cdn_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age=10000, &&&&&', false],
+            ['Cache-Control', 'no-store']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'CDN should ignore a `CDN-Cache-Control` that\'s an invalid Structured Field (wrong type)',
+      id: 'cdn-cc-invalid-sh-type-wrong',
+      depends_on: ['cdn-max-age'],
+      cdn_only: true,
+      requests: [
+        {
+          response_headers: [
+            ['CDN-Cache-Control', 'max-age="10000"', false],
+            ['Cache-Control', 'no-store']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'Does the CDN forward the `CDN-Cache-Control` response header?',
+      id: 'cdn-remove-header',
+      cdn_only: true,
+      kind: 'check',
+      requests: [
+        {
+          // only check for the header in expected_response_headers, so failing
+          // this is an assertion failure and not a setup error
+          response_headers: [
+            ['Cache-Control', 'max-age=10000'],
+            ['CDN-Cache-Control', 'foo', false],
+            ['Expires', 10000],
+            ['Date', 0]
+          ],
+          expected_response_headers: [
+            ['CDN-Cache-Control', 'foo']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does the CDN send `Age` when `CDN-Cache-Control: max-age` exceeds `Cache-Control: max-age`?',
+      id: 'cdn-remove-age-exceed',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1'],
+            ['CDN-Cache-Control', 'max-age=10000'],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_response_headers: [
+            'Age'
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does the CDN preserve `Date` when `CDN-Cache-Control: max-age` exceeds `Cache-Control: max-age`?',
+      id: 'cdn-date-update-exceed',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1'],
+            ['CDN-Cache-Control', 'max-age=10000'],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_response_headers: [
+            ['Date', 0]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does the CDN preserve `Expires` when `CDN-Cache-Control: max-age` exceeds `Cache-Control: max-age`?',
+      id: 'cdn-expires-update-exceed',
+      cdn_only: true,
+      depends_on: ['cdn-max-age'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=1'],
+            ['Expires', 1],
+            ['CDN-Cache-Control', 'max-age=10000'],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_response_headers: [
+            ['Expires', 1]
+          ]
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/conditional-etag.mjs b/test/fixtures/cache-tests/tests/conditional-etag.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/conditional-etag.mjs
@@ -0,0 +1,456 @@
+import * as templates from './lib/templates.mjs'
+
+export default {
+  name: 'Conditional Requests: If-None-Match and ETag',
+  id: 'conditional-inm',
+  description: 'These tests check handling of conditional requests using `If-None-Match` and `ETag`.',
+  spec_anchors: ['validation.model'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache responds to `If-None-Match` with a `304` when holding a fresh response with a matching strong `ETag`',
+      id: 'conditional-etag-strong-respond',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must include `ETag` in a `304 Not Modified`',
+      id: 'conditional-304-etag',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304,
+          expected_response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must give precedence to `If-None-Match` over `If-Modified-Since`',
+      id: 'conditional-etag-precedence',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Last-Modified', -5000],
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdef"'],
+            ['If-Modified-Since', -1]
+          ],
+          magic_ims: true,
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache responds to `If-None-Match` with a `304` when holding a fresh response with a matching strong `ETag` containing obs-text?',
+      id: 'conditional-etag-strong-respond-obs-text',
+      kind: 'check',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdefü"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdefü"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304,
+          expected_response_headers: [
+            ['ETag', '"abcdefü"']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache responds to unquoted `If-None-Match` with a `304` when holding a fresh response with a matching strong `ETag` that is quoted',
+      id: 'conditional-etag-quoted-respond-unquoted',
+      kind: 'check',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', 'abcdef']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache responds to unquoted `If-None-Match` with a `304` when holding a fresh response with a matching strong `ETag` that is unquoted',
+      id: 'conditional-etag-unquoted-respond-unquoted',
+      kind: 'check',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', 'abcdef']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', 'abcdef']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache responds to quoted `If-None-Match` with a `304` when holding a fresh response with a matching strong `ETag` that is unquoted',
+      id: 'conditional-etag-unquoted-respond-quoted',
+      kind: 'check',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', 'abcdef']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-None-Match` with a `304` when holding a fresh response with a matching weak `ETag`',
+      id: 'conditional-etag-weak-respond',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', 'W/"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', 'W/"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache responds to `If-None-Match` with a `304` when holding a fresh response with a matching weak `ETag`, and the entity-tag weakness flag is lowercase',
+      id: 'conditional-etag-weak-respond-lowercase',
+      kind: 'check',
+      depends_on: ['conditional-etag-weak-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', 'w/"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', 'w/"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache responds to `If-None-Match` with a `304` when holding a fresh response with a matching weak `ETag`, and the entity-tag weakness flag uses `\\` instead of `/`',
+      id: 'conditional-etag-weak-respond-backslash',
+      kind: 'check',
+      depends_on: ['conditional-etag-weak-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', 'W\\"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', 'W\\"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache responds to `If-None-Match` with a `304` when holding a fresh response with a matching weak `ETag`, and the entity-tag weakness flag omits `/`',
+      id: 'conditional-etag-weak-respond-omit-slash',
+      depends_on: ['conditional-etag-weak-respond'],
+      kind: 'check',
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', 'W"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', 'W"abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-None-Match` with a `304` when it contains multiple entity-tags (first one)',
+      id: 'conditional-etag-strong-respond-multiple-first',
+      kind: 'optimal',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdef", "1234", "5678"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-None-Match` with a `304` when it contains multiple entity-tags (middle one)',
+      id: 'conditional-etag-strong-respond-multiple-second',
+      kind: 'optimal',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"1234", "abcdef", "5678"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-None-Match` with a `304` when it contains multiple entity-tags (last one)',
+      id: 'conditional-etag-strong-respond-multiple-last',
+      kind: 'optimal',
+      depends_on: ['conditional-etag-strong-respond'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-None-Match', '"1234", "5678", "abcdef"']
+          ],
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must include stored response headers identified by `Vary` in a conditional request it generates',
+      id: 'conditional-etag-vary-headers',
+      requests: [
+        {
+          request_headers: [
+            ['Abc', '123']
+          ],
+          response_headers: [
+            ['Expires', 1],
+            ['ETag', '"abcdef"'],
+            ['Date', 0],
+            ['Vary', 'Abc']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          request_headers: [
+            ['Abc', '123']
+          ],
+          expected_type: 'etag_validated',
+          expected_request_headers: [
+            ['Abc', '123']
+          ],
+          setup_tests: ['expected_type']
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not use a stored `ETag` to validate when the presented `Vary`ing request header differs',
+      id: 'conditional-etag-vary-headers-mismatch',
+      depends_on: ['conditional-etag-vary-headers', 'vary-no-match'],
+      requests: [
+        {
+          request_headers: [
+            ['Abc', '123']
+          ],
+          response_headers: [
+            ['Expires', 10000],
+            ['ETag', '"abcdef"'],
+            ['Date', 0],
+            ['Vary', 'Abc']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          request_headers: [
+            ['Abc', '456']
+          ],
+          expected_request_headers_missing: [
+            ['If-None-Match', '"abcdef"']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache generates a `If-None-Match` request when holding a stale response with a matching strong `ETag`',
+      id: 'conditional-etag-strong-generate',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age-stale'],
+      requests: [
+        templates.becomeStale({
+          response_headers: [
+            ['ETag', '"abcdef"']
+          ]
+        }),
+        {
+          expected_request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ],
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache generates a `If-None-Match` request when holding a stale response with a matching weak `ETag`',
+      id: 'conditional-etag-weak-generate-weak',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age-stale'],
+      requests: [
+        templates.becomeStale({
+          response_headers: [
+            ['ETag', 'W/"abcdef"']
+          ]
+        }),
+        {
+          expected_request_headers: [
+            ['If-None-Match', 'W/"abcdef"']
+          ],
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache generate a quoted `If-None-Match` request when holding a stale response with a matching, unquoted strong `ETag`?',
+      id: 'conditional-etag-strong-generate-unquoted',
+      kind: 'check',
+      depends_on: ['conditional-etag-strong-generate'],
+      requests: [
+        templates.becomeStale({
+          response_headers: [
+            ['ETag', 'abcdef']
+          ]
+        }),
+        {
+          expected_request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ],
+          expected_type: 'etag_validated'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache forward `If-None-Match` request header when no stored response is available?',
+      id: 'conditional-etag-forward',
+      kind: 'check',
+      requests: [
+        {
+          request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ],
+          expected_request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache add quotes to an unquoted `If-None-Match` request when forwarding it?',
+      id: 'conditional-etag-forward-unquoted',
+      depends_on: ['conditional-etag-forward'],
+      kind: 'check',
+      requests: [
+        {
+          request_headers: [
+            ['If-None-Match', 'abcdef']
+          ],
+          expected_request_headers: [
+            ['If-None-Match', '"abcdef"']
+          ]
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/conditional-lm.mjs b/test/fixtures/cache-tests/tests/conditional-lm.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/conditional-lm.mjs
@@ -0,0 +1,119 @@
+import * as templates from './lib/templates.mjs'
+
+export default {
+  name: 'Conditional Requests: If-Modified-Since and Last-Modified',
+  id: 'conditional-lm',
+  description: 'These tests check handling of conditional requests using `If-Modified-Since` and `Last-Modified`.',
+  spec_anchors: ['validation.model'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache responds to `If-Modified-Since` with a `304` when holding a fresh response with a matching `Last-Modified`',
+      id: 'conditional-lm-fresh',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Last-Modified', -3000]
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-Modified-Since', -3000]
+          ],
+          magic_ims: true,
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-Modified-Since` with a `304` when holding a fresh response with an earlier `Last-Modified`',
+      id: 'conditional-lm-fresh-earlier',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Last-Modified', -3000]
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-Modified-Since', -2000]
+          ],
+          magic_ims: true,
+          expected_type: 'cached',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-Modified-Since` with a `304` when holding a stale response with a matching `Last-Modified`, after validation',
+      id: 'conditional-lm-stale',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age-stale'],
+      browser_skip: true,
+      requests: [
+        templates.becomeStale({
+          response_headers: [
+            ['Last-Modified', -3000]
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-Modified-Since', -3000]
+          ],
+          magic_ims: true,
+          expected_type: 'lm_validated',
+          expected_status: 304
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-Modified-Since` with a `304` when holding a newer fresh response with no `Last-Modified`',
+      id: 'conditional-lm-fresh-no-lm',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['If-Modified-Since', -3000]
+          ],
+          magic_ims: true,
+          expected_type: 'cached',
+          expected_status: 304,
+          setup_tests: ['expected_type']
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache responds to `If-Modified-Since` with a `304` when holding a newer fresh response when IMS uses an equivalent rfc850 date',
+      id: 'conditional-lm-fresh-rfc850',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      browser_skip: true,
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Last-Modified', -3000]
+          ]
+        }),
+        {
+          request_headers: [
+            ['If-Modified-Since', -3000]
+          ],
+          magic_ims: true,
+          rfc850date: ['if-modified-since'],
+          expected_type: 'cached',
+          expected_status: 304,
+          setup_tests: ['expected_type']
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/expires-freshness.mjs b/test/fixtures/cache-tests/tests/expires-freshness.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/expires-freshness.mjs
@@ -0,0 +1,154 @@
+export default
+
+{
+  name: 'Expires Freshness',
+  id: 'expires',
+  description: 'These tests check how caches calculate freshness using `Expires`.',
+  spec_anchors: ['expiration.model', 'field.expires'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache reuses a response with a future `Expires`',
+      id: 'freshness-expires-future',
+      kind: 'optimal',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 30 * 24 * 60 * 60],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with a past `Expires`',
+      id: 'freshness-expires-past',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', -30 * 24 * 60 * 60],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with a present `Expires`',
+      id: 'freshness-expires-present',
+      depends_on: ['freshness-none'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 0],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an `Expires` older than `Date`, both fast',
+      id: 'freshness-expires-old-date',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 300],
+            ['Date', 400]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (0)',
+      id: 'freshness-expires-invalid',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', '0', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with `Expires`, even if `Date` is invalid',
+      id: 'freshness-expires-invalid-date',
+      depends_on: ['freshness-expires-future'],
+      kind: 'optimal',
+      requests: [
+        {
+          response_headers: [
+            ['Date', 'foo', false],
+            ['Expires', 10]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response when the `Age` header is greater than its `Expires` minus `Date`, and `Date` is slow',
+      id: 'freshness-expires-age-slow-date',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', -10],
+            ['Expires', 10],
+            ['Age', '25']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response when the `Age` header is greater than its `Expires` minus `Date`, and `Date` is fast',
+      id: 'freshness-expires-age-fast-date',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 10],
+            ['Expires', 20],
+            ['Age', '15']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/expires-parse.mjs b/test/fixtures/cache-tests/tests/expires-parse.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/expires-parse.mjs
@@ -0,0 +1,301 @@
+export default
+
+{
+  name: 'Expires Parsing',
+  id: 'expires-parse',
+  description: 'These tests check how caches parse the `Expires` response header.',
+  spec_anchors: ['field.expires'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache reuses a response with an `Expires` that is exactly 32 bits',
+      id: 'freshness-expires-32bit',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Tue, 19 Jan 2038 14:14:08 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with an `Expires` that is far in the future',
+      id: 'freshness-expires-far-future',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Sun, 21 Nov 2286 04:46:39 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with a future `Expires` in obsolete RFC 850 format',
+      id: 'freshness-expires-rfc850',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thursday, 18-Aug-50 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with a future `Expires` in ANSI C\'s asctime() format',
+      id: 'freshness-expires-ansi-c',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu Aug  8 02:01:18 2050', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with a future `Expires` using wrong case (weekday)',
+      id: 'freshness-expires-wrong-case-weekday',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'THU, 18 Aug 2050 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with a future `Expires` using wrong case (month)',
+      id: 'freshness-expires-wrong-case-month',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 AUG 2050 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a response with a future `Expires` using wrong case (tz)',
+      id: 'freshness-expires-wrong-case-tz',
+      kind: 'optimal',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 2050 02:01:18 gMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (UTC)',
+      id: 'freshness-expires-invalid-utc',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 2050 02:01:18 UTC', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (other tz)',
+      id: 'freshness-expires-invalid-aest',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 2050 02:01:18 AEST', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (two-digit year)',
+      id: 'freshness-expires-invalid-2-digit-year',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 50 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (missing comma)',
+      id: 'freshness-expires-invalid-no-comma',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu 18 Aug 2050 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (multiple spaces)',
+      id: 'freshness-expires-invalid-multiple-spaces',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18  Aug  2050 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (date dashes)',
+      id: 'freshness-expires-invalid-date-dashes',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18-Aug-2050 02:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (time periods)',
+      id: 'freshness-expires-invalid-time-periods',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 2050 02.01.18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (1-digit hour)',
+      id: 'freshness-expires-invalid-1-digit-hour',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 2050 2:01:18 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse a response with an invalid `Expires` (multiple lines)',
+      id: 'freshness-expires-invalid-multiple-lines',
+      depends_on: ['freshness-expires-future'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 'Thu, 18 Aug 2050 2:01:18 GMT', false],
+            ['Expires', 'Thu, 18 Aug 2050 2:01:19 GMT', false],
+            ['Date', 0]
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/headers.mjs b/test/fixtures/cache-tests/tests/headers.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/headers.mjs
@@ -0,0 +1,77 @@
+import * as templates from './lib/templates.mjs'
+import * as utils from './lib/utils.mjs'
+import headerList from './lib/header-list.mjs'
+
+const tests = []
+
+tests.push({
+  name: '`Connection` header must inhibit a HTTP cache from storing listed headers',
+  id: 'headers-omit-headers-listed-in-Connection',
+  kind: 'required',
+  depends_on: ['freshness-max-age'],
+  requests: [
+    templates.fresh({
+      response_headers: [
+        ['Connection', 'a, b', false],
+        ['a', '1', false],
+        ['b', '2', false],
+        ['c', '3', false]
+      ]
+    }),
+    {
+      expected_type: 'cached',
+      expected_response_headers: [['c', '3']],
+      expected_response_headers_missing: ['a', 'b'],
+      setup_tests: ['expected_type', 'expected_response_headers']
+    }
+  ]
+})
+
+function checkStoreHeader (config) {
+  const id = `store-${config.name}`
+  const value = 'valB' in config ? config.valB : utils.httpContent(`${config.name}-store-value`)
+  const storeHeader = 'noStore' in config ? !config.noStore : true
+  const requirement = storeHeader ? 'must' : 'must not'
+  const expectedHeaders = storeHeader ? [[config.name, value]] : []
+  const unexpectedHeaders = storeHeader ? [] : [[config.name, value]]
+
+  const respHeaders = [
+    ['Date', 0],
+    [config.name, value, storeHeader]
+  ]
+  if (config.name !== 'Cache-Control') {
+    respHeaders.push(['Cache-Control', 'max-age=3600'])
+  }
+
+  tests.push({
+    name: `HTTP cache ${requirement} store \`${config.name}\` header field`,
+    id: `headers-${id}`,
+    kind: 'required',
+    depends_on: ['freshness-max-age'],
+    requests: [
+      {
+        response_headers: respHeaders,
+        setup: true,
+        pause_after: true,
+        check_body: 'checkBody' in config ? config.checkBody : true
+      },
+      {
+        expected_type: 'cached',
+        expected_response_headers: expectedHeaders,
+        expected_response_headers_missing: unexpectedHeaders,
+        setup_tests: ['expected_type'],
+        check_body: 'checkBody' in config ? config.checkBody : true
+      }
+    ]
+  })
+}
+
+headerList.forEach(checkStoreHeader)
+
+export default {
+  name: 'Storing Header Fields',
+  id: 'headers',
+  description: 'These tests examine how caches store headers in responses.',
+  spec_anchors: ['storing.fields'],
+  tests
+}
diff --git a/test/fixtures/cache-tests/tests/heuristic-freshness.mjs b/test/fixtures/cache-tests/tests/heuristic-freshness.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/heuristic-freshness.mjs
@@ -0,0 +1,95 @@
+import * as utils from './lib/utils.mjs'
+
+const tests = []
+
+function checkStatus (status) {
+  const succeed = status[0]
+  const code = status[1]
+  const phrase = status[2]
+  let body = status[3]
+  if (body === undefined) {
+    body = utils.httpContent(code)
+  }
+  const extra = status[4] || ''
+  const extraHdr = status[5]
+  const specAnchors = status[6] || []
+  let expectedType = 'not_cached'
+  let desired = 'HTTP cache must not reuse'
+  if (succeed === true) {
+    expectedType = 'cached'
+    desired = 'An optimal HTTP cache should reuse'
+  }
+  const responseHeaders = [
+    ['Last-Modified', -24 * 60 * 60],
+    ['Date', 0]
+  ]
+  if (extraHdr) {
+    responseHeaders.push(extraHdr)
+  }
+  tests.push({
+    name: `${desired} a \`${code} ${phrase}\` response with \`Last-Modified\` based upon heuristic freshness ${extra}`,
+    id: `heuristic-${code}-${expectedType}`,
+    kind: succeed ? 'optimal' : 'required',
+    spec_anchors: specAnchors,
+    requests: [{
+      response_status: [code, phrase],
+      response_headers: responseHeaders,
+      response_body: body,
+      setup: true
+    }, {
+      expected_type: expectedType,
+      response_status: [code, phrase],
+      response_body: body
+    }]
+  })
+}
+
+[
+  [true, 200, 'OK'],
+  [false, 201, 'Created'],
+  [false, 202, 'Accepted'],
+  [true, 203, 'Non-Authoritative Information'],
+  [true, 204, 'No Content', null],
+  [false, 403, 'Forbidden'],
+  [true, 404, 'Not Found'],
+  [true, 405, 'Method Not Allowed'],
+  [true, 410, 'Gone'],
+  [true, 414, 'URI Too Long'],
+  [true, 501, 'Not Implemented'],
+  [false, 502, 'Bad Gateway'],
+  [false, 503, 'Service Unavailable'],
+  [false, 504, 'Gateway Timeout'],
+  [false, 599, 'Unknown', undefined, 'when `Cache-Control: public` is not present', undefined, ['cache-response-directive.public']],
+  [true, 599, 'Unknown', undefined, 'when `Cache-Control: public` is present', ['Cache-Control', 'public'], ['cache-response-directive.public']]
+].forEach(checkStatus)
+
+function checkHeuristic (delta) {
+  tests.push({
+    name: `Does HTTP cache consider a \`Last-Modified\` ${delta} seconds ago heuristically fresh?`,
+    id: `heuristic-delta-${delta}`,
+    kind: 'check',
+    requests: [{
+      response_headers: [
+        ['Last-Modified', -delta],
+        ['Date', 0]
+      ],
+      setup: true,
+      pause_after: true
+    },
+    {
+      expected_type: 'cached'
+    }]
+  })
+}
+
+[
+  5, 10, 30, 60, 300, 600, 1200, 1800, 3600, 3600 * 12, 3600 * 24
+].forEach(checkHeuristic)
+
+export default {
+  name: 'Heuristic Freshness',
+  id: 'heuristic',
+  description: 'These tests check how caches handle heuristic freshness.',
+  spec_anchors: ['heuristic.freshness'],
+  tests
+}
diff --git a/test/fixtures/cache-tests/tests/index.mjs b/test/fixtures/cache-tests/tests/index.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/index.mjs
@@ -0,0 +1,26 @@
+import ccParse from './cc-parse.mjs'
+import ccRequest from './cc-request.mjs'
+import ccResponse from './cc-response.mjs'
+import ccFreshness from './cc-freshness.mjs'
+import ageParse from './age-parse.mjs'
+import pragma from './pragma.mjs'
+import expiresParse from './expires-parse.mjs'
+import expires from './expires-freshness.mjs'
+import stale from './stale.mjs'
+import heuristic from './heuristic-freshness.mjs'
+import methods from './method.mjs'
+import statuses from './status.mjs'
+import vary from './vary.mjs'
+import varyParse from './vary-parse.mjs'
+import conditionalLm from './conditional-lm.mjs'
+import conditionalEtag from './conditional-etag.mjs'
+import headers from './headers.mjs'
+import update304 from './update304.mjs'
+import updateHead from './updateHead.mjs'
+import invalidation from './invalidation.mjs'
+import partial from './partial.mjs'
+import auth from './authorization.mjs'
+import other from './other.mjs'
+import cdncc from './cdn-cache-control.mjs'
+
+export default [ccFreshness, ccParse, ageParse, expires, expiresParse, ccResponse, stale, heuristic, methods, statuses, ccRequest, pragma, vary, varyParse, conditionalLm, conditionalEtag, headers, update304, updateHead, invalidation, partial, auth, other, cdncc]
diff --git a/test/fixtures/cache-tests/tests/invalidation.mjs b/test/fixtures/cache-tests/tests/invalidation.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/invalidation.mjs
@@ -0,0 +1,121 @@
+import { makeTemplate, fresh } from './lib/templates.mjs'
+
+const contentLocation = makeTemplate({
+  filename: 'content_location_target',
+  response_headers: [
+    ['Cache-Control', 'max-age=100000'],
+    ['Last-Modified', 0],
+    ['Date', 0]
+  ]
+})
+
+const location = makeTemplate({
+  filename: 'location_target',
+  response_headers: [
+    ['Cache-Control', 'max-age=100000'],
+    ['Last-Modified', 0],
+    ['Date', 0]
+  ]
+})
+
+const lclResponse = makeTemplate({
+  response_headers: [
+    ['Location', 'location_target'],
+    ['Content-Location', 'content_location_target']
+  ],
+  magic_locations: true
+})
+
+const tests = []
+
+function checkInvalidation (method) {
+  tests.push({
+    name: `HTTP cache must invalidate the URL after a successful response to a \`${method}\` request`,
+    id: `invalidate-${method}`,
+    depends_on: ['freshness-max-age'],
+    requests: [
+      fresh({}), {
+        request_method: method,
+        request_body: 'abc',
+        setup: true
+      }, {
+        expected_type: 'not_cached'
+      }
+    ]
+  })
+  tests.push({
+    name: `An optimal HTTP cache does not invalidate the URL after a failed response to a \`${method}\` request`,
+    id: `invalidate-${method}-failed`,
+    kind: 'optimal',
+    depends_on: [`invalidate-${method}`],
+    requests: [
+      fresh({}), {
+        request_method: method,
+        request_body: 'abc',
+        response_status: [500, 'Internal Server Error'],
+        setup: true
+      }, {
+        expected_type: 'cached'
+      }
+    ]
+  })
+}
+
+function checkLocationInvalidation (method) {
+  tests.push({
+    name: `Does HTTP cache invalidate \`Location\` URL after a successful response to a \`${method}\` request?`,
+    id: `invalidate-${method}-location`,
+    kind: 'check',
+    depends_on: [`invalidate-${method}`],
+    requests: [
+      location({
+        setup: true
+      }), lclResponse({
+        request_method: 'POST',
+        request_body: 'abc',
+        setup: true
+      }), location({
+        expected_type: 'not_cached'
+      })
+    ]
+  })
+}
+
+function checkClInvalidation (method) {
+  tests.push({
+    name: `Does HTTP cache must invalidate \`Content-Location\` URL after a successful response to a \`${method}\` request?`,
+    id: `invalidate-${method}-cl`,
+    kind: 'check',
+    depends_on: [`invalidate-${method}`],
+    requests: [
+      contentLocation({
+        setup: true
+      }), lclResponse({
+        request_method: method,
+        request_body: 'abc',
+        setup: true
+      }), contentLocation({
+        expected_type: 'not_cached'
+      })
+    ]
+  })
+}
+
+const methods = [
+  'POST',
+  'PUT',
+  'DELETE',
+  'M-SEARCH'
+]
+
+methods.forEach(checkInvalidation)
+methods.forEach(checkLocationInvalidation)
+methods.forEach(checkClInvalidation)
+
+export default {
+  name: 'Cache Invalidation',
+  id: 'invalidation',
+  description: 'These tests check how caches support  invalidation, including when it is triggered by the `Location` and `Content-Location` response headers.',
+  spec_anchors: ['invalidation'],
+  tests
+}
diff --git a/test/fixtures/cache-tests/tests/lib/header-list.mjs b/test/fixtures/cache-tests/tests/lib/header-list.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/lib/header-list.mjs
@@ -0,0 +1,135 @@
+export default [
+  {
+    name: 'Test-Header',
+    reqUpdate: true
+  },
+  {
+    name: 'X-Test-Header',
+    reqUpdate: true
+  },
+  {
+    name: 'Content-Foo',
+    reqUpdate: true
+  },
+  {
+    name: 'X-Content-Foo',
+    reqUpdate: true
+  },
+  {
+    name: 'Cache-Control',
+    valA: 'max-age=1',
+    valB: 'max-age=3600',
+    reqUpdate: true
+  },
+  {
+    name: 'Connection',
+    noStore: true
+  },
+  {
+    name: 'Content-Encoding'
+  },
+  {
+    name: 'Content-Length',
+    valA: '36',
+    valB: '10',
+    noUpdate: true,
+    checkBody: false
+  },
+  {
+    name: 'Content-Location',
+    valA: '/foo',
+    valB: '/bar'
+  },
+  {
+    name: 'Content-MD5',
+    valA: 'rL0Y20zC+Fzt72VPzMSk2A==',
+    valB: 'N7UdGUp1E+RbVvZSTy1R8g=='
+  },
+  {
+    name: 'Content-Range'
+  },
+  {
+    name: 'Content-Security-Policy',
+    valA: 'default-src \'self\'',
+    valB: 'default-src \'self\' cdn.example.com'
+  },
+  {
+    name: 'Content-Type',
+    valA: 'text/plain',
+    valB: 'text/plain;charset=utf-8'
+  },
+  {
+    name: 'Clear-Site-Data',
+    valA: 'cache',
+    valB: 'cookies'
+  },
+  {
+    name: 'ETag',
+    valA: '"abcdef"',
+    valB: '"ghijkl"'
+  },
+  {
+    name: 'Expires',
+    valA: 'Fri, 01 Jan 2038 01:01:01 GMT',
+    valB: 'Mon, 11 Jan 2038 11:11:11 GMT'
+  },
+  {
+    name: 'Keep-Alive',
+    noStore: true
+  },
+  {
+    name: 'Proxy-Authenticate',
+    noStore: true
+  },
+  {
+    name: 'Proxy-Authentication-Info',
+    noStore: true
+  },
+  {
+    name: 'Proxy-Authorization',
+    noStore: true
+  },
+  {
+    name: 'Proxy-Connection',
+    noStore: true
+  },
+  {
+    name: 'Public-Key-Pins'
+  },
+  {
+    name: 'Set-Cookie',
+    valA: 'a=b',
+    valB: 'a=c'
+  },
+  {
+    name: 'Set-Cookie2',
+    valA: 'a=b',
+    valB: 'a=c'
+  },
+  {
+    name: 'TE',
+    noStore: true
+  },
+  //  {
+  //    name: 'Trailer',
+  //    noStore: true
+  //  },
+  {
+    name: 'Transfer-Encoding',
+    noStore: true
+  },
+  {
+    name: 'Upgrade',
+    noStore: true
+  },
+  {
+    name: 'X-Frame-Options',
+    valA: 'deny',
+    valB: 'sameorigin'
+  },
+  {
+    name: 'X-XSS-Protection',
+    valA: '1',
+    valB: '1; mode=block'
+  }
+]
diff --git a/test/fixtures/cache-tests/tests/lib/templates.mjs b/test/fixtures/cache-tests/tests/lib/templates.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/lib/templates.mjs
@@ -0,0 +1,74 @@
+/*
+makeTemplate(template)
+
+templates take an optional request object; the template
+will be updated with the request object in the following manner:
+
+- Object members will be assigned from the request
+- Array members will be concatonated from the request
+- Other members will be updated from the request
+*/
+export function makeTemplate (template) {
+  return function (request) {
+    return mergeDeep({}, template, request)
+  }
+}
+
+function isObject (item) {
+  return (item && typeof item === 'object' && !Array.isArray(item))
+}
+
+function mergeDeep (target, ...sources) {
+  if (!sources.length) return target
+  const source = sources.shift()
+
+  if (isObject(target) && isObject(source)) {
+    for (const key in source) {
+      if (isObject(source[key])) {
+        if (!target[key]) Object.assign(target, { [key]: {} })
+        mergeDeep(target[key], source[key])
+      } else if (Array.isArray(source[key])) {
+        if (!target[key]) Object.assign(target, { [key]: [] })
+        Object.assign(target, { [key]: target[key].concat(source[key]) })
+      } else {
+        Object.assign(target, { [key]: source[key] })
+      }
+    }
+  }
+
+  return mergeDeep(target, ...sources)
+}
+
+/*
+ Templates below are shared between multiple suites;
+ suite-specific tests should go in that file.
+*/
+
+export const fresh = makeTemplate({
+  response_headers: [
+    ['Cache-Control', 'max-age=100000'],
+    ['Date', 0]
+  ],
+  setup: true,
+  pause_after: true
+})
+
+export const stale = makeTemplate({
+  response_headers: [
+    ['Expires', -5000],
+    ['Last-Modified', -100000],
+    ['Date', 0]
+  ],
+  setup: true,
+  pause_after: true
+})
+
+export const becomeStale = makeTemplate({
+  response_headers: [
+    ['Cache-Control', 'max-age=2'],
+    ['Date', 0],
+    ['Template-A', '1']
+  ],
+  setup: true,
+  pause_after: true
+})
diff --git a/test/fixtures/cache-tests/tests/lib/utils.mjs b/test/fixtures/cache-tests/tests/lib/utils.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/lib/utils.mjs
@@ -0,0 +1,20 @@
+const contentSeed = 1
+const contentStore = {}
+export function httpContent (csKey, contentLength = 15) {
+  if (csKey in contentStore) {
+    return contentStore[csKey]
+  } else {
+    let keySeed = 0
+    for (let i = 0; i < csKey.length; i++) {
+      keySeed += csKey.charCodeAt(i)
+    }
+    const contents = []
+    for (let i = 0; i < contentLength; ++i) {
+      const idx = ((i * keySeed * contentSeed) % 26) + 97
+      contents.push(String.fromCharCode(idx))
+    }
+    const content = contents.join('')
+    contentStore[csKey] = content
+    return content
+  }
+}
diff --git a/test/fixtures/cache-tests/tests/method.mjs b/test/fixtures/cache-tests/tests/method.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/method.mjs
@@ -0,0 +1,35 @@
+export default
+
+{
+  name: 'Method-related Caching Requirements',
+  id: 'method',
+  description: 'These tests check how caches handle different HTTP methods.',
+  spec_anchors: ['response.cacheability'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache reuses a stored `POST` response (that has `Content-Location` with the same URL and explicit freshness) for subsequent `GET` requests',
+      id: 'method-POST',
+      kind: 'optimal',
+      requests: [
+        {
+          request_method: 'POST',
+          request_body: '12345',
+          request_headers: [
+            ['Content-Type', 'text/plain']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Content-Location', ''],
+            ['Date', 0]
+          ],
+          magic_locations: true,
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/other.mjs b/test/fixtures/cache-tests/tests/other.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/other.mjs
@@ -0,0 +1,239 @@
+import * as templates from './lib/templates.mjs'
+import * as utils from './lib/utils.mjs'
+
+export default
+
+{
+  name: 'Other Caching Requirements',
+  id: 'other',
+  description: 'These tests check miscellaneous HTTP cache behaviours. ',
+  tests: [
+    {
+      name: 'HTTP cache must generate an `Age` header field when using a stored response.',
+      id: 'other-age-gen',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['field.age', 'constructing.responses.from.caches'],
+      requests: [
+        templates.fresh({}),
+        {
+          expected_type: 'cached',
+          expected_response_headers: [
+            ['Age', '>', 2]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache insert an `Age` header field when there is delay generating the response?',
+      id: 'other-age-delay',
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Date', 0]
+          ],
+          response_pause: 5,
+          expected_response_headers: [['age', '>', 0]]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must update the `Age` header field when freshness is based upon `Expires`',
+      id: 'other-age-update-expires',
+      depends_on: ['freshness-expires-future'],
+      spec_anchors: ['constructing.responses.from.caches', 'field.age'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 30 * 24 * 60 * 60],
+            ['Date', 0],
+            ['Age', '30']
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [
+            ['Age', '>', 32]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must update the `Age` header field when freshness is based upon `CC: max-age`',
+      id: 'other-age-update-max-age',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['constructing.responses.from.caches', 'field.age'],
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Age', '30']
+          ]
+        }),
+        {
+          expected_type: 'cached',
+          expected_response_headers: [
+            ['Age', '>', 32]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not update the `Date` header field',
+      id: 'other-date-update',
+      depends_on: ['freshness-max-age'],
+      spec_anchors: ['field.date'],
+      requests: [
+        templates.fresh({}),
+        {
+          expected_type: 'cached',
+          expected_response_headers: [
+            ['Date', 0]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not update the `Date` header field when `Expires` is present',
+      id: 'other-date-update-expires',
+      depends_on: ['freshness-expires-future'],
+      spec_anchors: ['field.date'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 30 * 24 * 60 * 60],
+            ['Date', 0]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [
+            ['Date', 0]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache leave the `Expires` header field alone?',
+      id: 'other-date-update-expires-update',
+      kind: 'check',
+      depends_on: ['freshness-expires-future'],
+      spec_anchors: ['field.date'],
+      requests: [
+        {
+          response_headers: [
+            ['Expires', 30 * 24 * 60 * 60],
+            ['Date', 0]
+          ],
+          pause_after: true,
+          setup: true
+        },
+        {
+          expected_type: 'cached',
+          expected_response_headers: [
+            ['Expires', 30 * 24 * 60 * 60]
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Different query arguments must be different cache keys',
+      id: 'query-args-different',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        templates.fresh({
+          query_arg: 'test=' + utils.httpContent('query-args-different-1')
+        }),
+        {
+          query_arg: 'test=' + utils.httpContent('query-args-different-2'),
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache should not be affected by the presence of a URL query',
+      id: 'query-args-same',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        templates.fresh({
+          query_arg: 'test=' + utils.httpContent('query-args-same')
+        }),
+        {
+          query_arg: 'test=' + utils.httpContent('query-args-same'),
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP heuristically cache a response with a `Content-Disposition: attachment` header?',
+      id: 'other-heuristic-content-disposition-attachment',
+      kind: 'check',
+      depends_on: ['heuristic-200-cached'],
+      requests: [
+        {
+          response_headers: [
+            ['Last-Modified', -100000],
+            ['Date', 0],
+            ['Content-Disposition', 'attachment; filename=example.txt']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP reuse a fresh response with a `Content-Disposition: attachment` header?',
+      id: 'other-fresh-content-disposition-attachment',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Content-Disposition', 'attachment; filename=example.txt']
+          ]
+        }),
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a fresh response with a `Set-Cookie` header',
+      id: 'other-set-cookie',
+      depends_on: ['freshness-max-age'],
+      kind: 'optimal',
+      requests: [
+        templates.fresh({
+          response_headers: [
+            ['Set-Cookie', 'a=b']
+          ]
+        }),
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a fresh response when the request has a `Cookie` header',
+      id: 'other-cookie',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['Cookie', 'a=b']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/partial.mjs b/test/fixtures/cache-tests/tests/partial.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/partial.mjs
@@ -0,0 +1,271 @@
+export default {
+  name: 'Combining Partial Content',
+  id: 'partial',
+  description: 'These tests check how caches handle partial content (also known as `Range` requests).',
+  spec_anchors: ['combining.responses'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache stores partial content and reuses it',
+      id: 'partial-store-partial-reuse-partial',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          response_status: [206, 'Partial Content'],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Content-Range', 'bytes 4-9/10']
+          ],
+          response_body: '01234',
+          expected_request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '01234'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores complete responses and serves smaller ranges from them (byte-range-spec)',
+      id: 'partial-store-complete-reuse-partial',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600']
+          ],
+          response_body: '01234567890',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=0-1']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '01'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores complete responses and serves smaller ranges from them (absent last-byte-pos)',
+      id: 'partial-store-complete-reuse-partial-no-last',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600']
+          ],
+          response_body: '01234567890',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=1-']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '1234567890'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores complete responses and serves smaller ranges from them (suffix-byte-range-spec)',
+      id: 'partial-store-complete-reuse-partial-suffix',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600']
+          ],
+          response_body: '0123456789A',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=-1']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: 'A'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores partial responses and serves smaller ranges from them (byte-range-spec)',
+      id: 'partial-store-partial-reuse-partial-byterange',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          response_status: [206, 'Partial Content'],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Content-Range', 'bytes 4-9/10']
+          ],
+          response_body: '01234',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=6-8']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '234'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores partial responses and serves smaller ranges from them (absent last-byte-pos)',
+      id: 'partial-store-partial-reuse-partial-absent',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          response_status: [206, 'Partial Content'],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Content-Range', 'bytes 4-9/10']
+          ],
+          response_body: '01234',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=6-']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '234'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores partial responses and serves smaller ranges from them (suffix-byte-range-spec)',
+      id: 'partial-store-partial-reuse-partial-suffix',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          response_status: [206, 'Partial Content'],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Content-Range', 'bytes 4-9/10']
+          ],
+          response_body: '01234',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=-1']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '4'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache stores partial content and completes it',
+      id: 'partial-store-partial-complete',
+      kind: 'optimal',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          request_headers: [
+            ['Range', 'bytes=-5']
+          ],
+          response_status: [206, 'Partial Content'],
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Content-Range', 'bytes 0-4/10']
+          ],
+          response_body: '01234',
+          setup: true
+        },
+        {
+          expected_request_headers: [
+            ['range', 'bytes=5-']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must use header fields from the new response',
+      id: 'partial-use-headers',
+      depends_on: ['partial-store-complete-reuse-partial'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['A', '1']
+          ],
+          response_body: '01234567890',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=0-1']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '01',
+          setup_tests: ['expected_type', 'expected_status', 'expected_response_text'],
+          response_headers: [
+            ['A', '2']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must preserve unupdated header fields from the stored response',
+      id: 'partial-use-stored-headers',
+      depends_on: ['partial-store-complete-reuse-partial'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['A', '1']
+          ],
+          response_body: '01234567890',
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Range', 'bytes=0-1']
+          ],
+          expected_type: 'cached',
+          expected_status: 206,
+          expected_response_text: '01',
+          setup_tests: ['expected_type', 'expected_status', 'expected_response_text'],
+          expected_response_headers: [
+            ['A', '1']
+          ]
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/pragma.mjs b/test/fixtures/cache-tests/tests/pragma.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/pragma.mjs
@@ -0,0 +1,97 @@
+import * as templates from './lib/templates.mjs'
+
+export default
+
+{
+  name: 'Pragma',
+  id: 'pragma',
+  description: 'These tests check how caches handle the deprecated `Pragma` header in reqeusts and responses. Note that This field is deprecated - it is not required to be supported.',
+  spec_anchors: ['field.pragma'],
+  tests: [
+    {
+      name: 'Does HTTP cache use a stored fresh response when request contains `Pragma: no-cache`?',
+      id: 'pragma-request-no-cache',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['Pragma', 'no-cache']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a stored fresh response when request contains `Pragma: unrecognised-extension`?',
+      id: 'pragma-request-extension',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        templates.fresh({}),
+        {
+          request_headers: [
+            ['Pragma', 'unrecognised-extension']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a stored and otherwise fresh response when it contains `Pragma: no-cache`?',
+      id: 'pragma-response-no-cache',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Pragma', 'no-cache']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache reuse a stored and heuristically fresh response when it contains `Pragma: no-cache`?',
+      id: 'pragma-response-no-cache-heuristic',
+      kind: 'check',
+      depends_on: ['heuristic-200-cached'],
+      requests: [
+        {
+          response_headers: [
+            ['Date', 0],
+            ['Last-Modified', -10000],
+            ['Pragma', 'no-cache']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache use a stored and otherwise fresh response when it contains `Pragma: unrecognised-extension`?',
+      id: 'pragma-response-extension',
+      kind: 'check',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=3600'],
+            ['Pragma', 'unrecognised-extension']
+          ],
+          setup: true
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/stale.mjs b/test/fixtures/cache-tests/tests/stale.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/stale.mjs
@@ -0,0 +1,177 @@
+import * as templates from './lib/templates.mjs'
+
+function makeStaleCheckCC (cc, sharedOnly, value) {
+  const shared = sharedOnly === true ? 'Shared ' : ''
+  return {
+    name: `${shared}HTTP cache must not serve stale stored response when prohibited by \`Cache-Control: ${cc}\``,
+    id: `stale-close-${cc}${value || ''}`,
+    browser_skip: sharedOnly,
+    depends_on: ['stale-close'],
+    spec_anchors: [`cache-response-directive.${cc}`],
+    requests: [
+      {
+        response_headers: [
+          ['Cache-Control', `max-age=2, ${cc}${value || ''}`]
+        ],
+        setup: true,
+        pause_after: true
+      },
+      {
+        disconnect: true,
+        expected_type: 'not_cached'
+      }
+    ]
+  }
+}
+
+export default {
+  name: 'Serving Stale',
+  id: 'stale',
+  description: 'These tests check how caches serve stale content.',
+  spec_anchors: ['serving.stale.responses'],
+  tests: [
+    {
+      name: 'Does HTTP cache serve stale stored response when server closes the connection?',
+      id: 'stale-close',
+      depends_on: ['freshness-max-age-stale'],
+      kind: 'check',
+      requests: [
+        templates.becomeStale({}),
+        {
+          disconnect: true,
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache serve stale stored response when server sends a `503 Service Unavailable`?',
+      id: 'stale-503',
+      depends_on: ['freshness-max-age-stale'],
+      kind: 'check',
+      requests: [
+        templates.becomeStale({}),
+        {
+          response_status: [503, 'Service Unavailable'],
+          expected_status: 200,
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal cache serves stale stored response with [`Cache-Control: stale-while-revalidate`](https://httpwg.org/specs/rfc5861.html)',
+      id: 'stale-while-revalidate',
+      depends_on: ['freshness-max-age-stale'],
+      kind: 'optimal',
+      requests: [
+        {
+          setup: true,
+          pause_after: true,
+          response_headers: [
+            ['Cache-Control', 'max-age=1, stale-while-revalidate=3600'],
+            ['ETag', '"abc"']
+          ]
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not serve stale stored response after the [`stale-while-revalidate`](https://httpwg.org/specs/rfc5861.html) window',
+      id: 'stale-while-revalidate-window',
+      depends_on: ['stale-while-revalidate'],
+      requests: [
+        {
+          setup: true,
+          pause_after: true,
+          response_headers: [
+            ['Cache-Control', 'max-age=1, stale-while-revalidate=4'],
+            ['ETag', '"abc"']
+          ]
+        },
+        {
+          setup: true,
+          pause_after: true,
+          expected_type: 'cached'
+        },
+        {
+          expected_response_headers: [
+            ['client-request-count', '3']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache serve stale stored response when server sends `Cache-Control: stale-if-error` and subsequently closes the connection?',
+      id: 'stale-sie-close',
+      depends_on: ['freshness-max-age-stale'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2, stale-if-error=60']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          disconnect: true,
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache serve stale stored response when server sends `Cache-Control: stale-if-error` and subsequently a `503 Service Unavailable`?',
+      id: 'stale-sie-503',
+      depends_on: ['freshness-max-age-stale'],
+      kind: 'check',
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=2, stale-if-error=60']
+          ],
+          setup: true,
+          pause_after: true
+        },
+        {
+          disconnect: true,
+          expected_type: 'cached'
+        }
+      ]
+    },
+    makeStaleCheckCC('must-revalidate', false),
+    makeStaleCheckCC('proxy-revalidate', true),
+    makeStaleCheckCC('no-cache', false),
+    makeStaleCheckCC('s-maxage', true, '=2'),
+    {
+      name: 'Does HTTP cache generate a `Warning` header when using a response that was stored already stale?',
+      id: 'stale-warning-stored',
+      kind: 'check',
+      depends_on: ['stale-close'],
+      requests: [
+        templates.stale({}),
+        {
+          disconnect: true,
+          expected_type: 'cached',
+          expected_response_headers: ['warning'],
+          setup_tests: ['expected_type']
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache generate a `Warning` header when using a stored response that became stale?',
+      id: 'stale-warning-become',
+      kind: 'check',
+      depends_on: ['stale-close'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          disconnect: true,
+          expected_type: 'cached',
+          expected_response_headers: ['warning'],
+          setup_tests: ['expected_type']
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/status.mjs b/test/fixtures/cache-tests/tests/status.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/status.mjs
@@ -0,0 +1,118 @@
+import * as templates from './lib/templates.mjs'
+import * as utils from './lib/utils.mjs'
+
+const tests = []
+
+function checkStatus (status) {
+  const code = status[0]
+  const phrase = status[1]
+  let body = status[2]
+  if (body === undefined) {
+    body = utils.httpContent(code)
+  }
+  const is3xx = code > 299 && code < 400
+  tests.push({
+    name: 'An optimal HTTP cache reuses a fresh `' + code + '` response with explict freshness',
+    id: `status-${code}-fresh`,
+    kind: 'optimal',
+    depends_on: ['freshness-max-age'],
+    browser_skip: is3xx,
+    requests: [
+      templates.fresh({
+        response_status: [code, phrase],
+        response_body: body,
+        redirect: 'manual'
+      }), {
+        expected_type: 'cached',
+        response_status: [code, phrase],
+        redirect: 'manual',
+        response_body: body
+      }
+    ]
+  })
+  tests.push({
+    name: 'HTTP cache must not reuse a stale `' + code + '` response with explicit freshness',
+    id: `status-${code}-stale`,
+    depends_on: [`status-${code}-fresh`],
+    browser_skip: is3xx,
+    requests: [
+      templates.stale({
+        response_status: [code, phrase],
+        response_body: body,
+        redirect: 'manual',
+        setup: true
+      }), {
+        expected_type: 'not_cached',
+        redirect: 'manual',
+        response_body: body
+      }
+    ]
+  })
+}
+[
+  [200, 'OK'],
+  [203, 'Non-Authoritative Information'],
+  [204, 'No Content', null],
+  [299, 'Whatever'],
+  [301, 'Moved Permanently'],
+  [302, 'Found'],
+  [303, 'See Other'],
+  [307, 'Temporary Redirect'],
+  [308, 'Permanent Redirect'],
+  [400, 'Bad Request'],
+  [404, 'Not Found'],
+  [410, 'Gone'],
+  [499, 'Whatever'],
+  [500, 'Internal Server Error'],
+  [502, 'Bad Gateway'],
+  [503, 'Service Unavailable'],
+  [504, 'Gateway Timeout'],
+  [599, 'Whatever']
+].forEach(checkStatus)
+
+tests.push({
+  name: 'HTTP cache must not reuse a fresh response with an unrecognised status code and `Cache-Control: no-store, must-understand`',
+  id: 'status-599-must-understand',
+  depends_on: ['status-599-fresh'],
+  spec_anchors: ['cache-response-directive.must-understand'],
+  requests: [
+    {
+      response_status: [599, 'Whatever'],
+      response_headers: [
+        ['Cache-Control', 'max-age=3600, no-store, must-understand']
+      ],
+      setup: true
+    },
+    {
+      expected_type: 'not_cached'
+    }
+  ]
+})
+
+tests.push({
+  name: 'An optimal HTTP cache reuses a fresh response with a recognised status code and `Cache-Control: no-store, must-understand`',
+  id: 'status-200-must-understand',
+  kind: 'optimal',
+  depends_on: ['status-200-fresh', 'cc-resp-no-store-fresh'],
+  spec_anchors: ['cache-response-directive.must-understand'],
+  requests: [
+    {
+      response_status: [200, 'OK'],
+      response_headers: [
+        ['Cache-Control', 'max-age=3600, no-store, must-understand']
+      ],
+      setup: true
+    },
+    {
+      expected_type: 'cached'
+    }
+  ]
+})
+
+export default {
+  name: 'Status Code Cacheability',
+  id: 'status',
+  description: 'These tests check to see if a cache will store and reuse various status codes when they have explicit freshness information associated with them.',
+  spec_anchors: ['response.cacheability'],
+  tests
+}
diff --git a/test/fixtures/cache-tests/tests/update304.mjs b/test/fixtures/cache-tests/tests/update304.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/update304.mjs
@@ -0,0 +1,124 @@
+import * as utils from './lib/utils.mjs'
+import headerList from './lib/header-list.mjs'
+
+const tests = []
+
+// first, check to see that the cache actually returns a stored header
+const storedHeader = 'Test-Header'
+const valueA = utils.httpContent(`${storedHeader}-value-A`)
+const lm1 = 'Wed, 01 Jan 2020 00:00:00 GMT'
+tests.push({
+  name: `HTTP cache must return stored \`${storedHeader}\` from a \`304\` that omits it`,
+  id: `304-lm-use-stored-${storedHeader}`,
+  requests: [
+    {
+      response_headers: [
+        ['Cache-Control', 'max-age=2'],
+        ['Last-Modified', lm1],
+        ['Date', 0],
+        [storedHeader, valueA]
+      ],
+      setup: true,
+      pause_after: true
+    },
+    {
+      response_headers: [
+        ['Last-Modified', lm1],
+        ['Date', 0]
+      ],
+      expected_type: 'lm_validated',
+      expected_response_headers: [
+        [storedHeader, valueA]
+      ],
+      setup_tests: ['expected_type']
+    }
+  ]
+})
+
+// now check headers in the list
+function check304 (config) {
+  if (config.noStore) return
+  config.valueA = config.valA || utils.httpContent(`${config.name}-value-A`)
+  config.valueB = config.valB || utils.httpContent(`${config.name}-value-B`)
+  if (config.noUpdate === true) {
+    config.expectedValue = config.valueA
+    config.requirement = 'HTTP cache must not'
+    config.punctuation = ''
+    config.kind = 'required'
+  } else if (config.reqUpdate === true) {
+    config.expectedValue = config.valueB
+    config.requirement = 'HTTP cache must'
+    config.punctuation = ''
+    config.kind = 'required'
+  } else {
+    config.expectedValue = config.valueB
+    config.requirement = 'Does HTTP cache'
+    config.punctuation = '?'
+    config.kind = 'check'
+  }
+  config.etagVal = utils.httpContent(`${config.name}-etag-1`)
+  config.etag = `"${config.etagVal}"`
+  config.lm = 'Wed, 01 Jan 2020 00:00:00 GMT'
+
+  tests.push({
+    name: `${config.requirement} update and return \`${config.name}\` from a \`304\`${config.punctuation}`,
+    id: `304-etag-update-response-${config.name}`,
+    kind: config.kind,
+    depends_on: [`304-lm-use-stored-${storedHeader}`],
+    requests: makeRequests(config, 'ETag', config.etag)
+  })
+}
+
+function makeRequests (config, validatorType, validatorValue) {
+  return [
+    {
+      response_headers: makeResponse(config, config.valueA, validatorType, validatorValue),
+      setup: true,
+      pause_after: true,
+      check_body: 'checkBody' in config ? config.checkBody : true
+    },
+    {
+      response_headers: makeResponse(config, config.valueB, validatorType, validatorValue),
+      expected_type: validatorType === 'ETag' ? 'etag_validated' : 'lm_validated',
+      setup_tests: ['expected_type'],
+      expected_response_headers: [
+        [config.name, config.expectedValue]
+      ],
+      check_body: 'checkBody' in config ? config.checkBody : true
+    },
+    {
+      response_headers: makeResponse(config, config.expectedValue),
+      expected_type: 'cached',
+      setup_tests: ['expected_type'],
+      expected_response_headers: [
+        [config.name, config.expectedValue]
+      ],
+      check_body: 'checkBody' in config ? config.checkBody : true
+    }
+  ]
+}
+
+function makeResponse (config, value, validatorType, validatorValue) {
+  const checkHeader = 'noUpdate' in config ? !config.noUpdate : true
+  const responseHeaders = [
+    ['Date', 0],
+    [config.name, value, checkHeader]
+  ]
+  if (config.name !== 'Cache-Control') {
+    responseHeaders.push(['Cache-Control', 'max-age=2'])
+  }
+  if (validatorType && validatorType !== config.name) {
+    responseHeaders.push([validatorType, validatorValue])
+  }
+  return responseHeaders
+}
+
+headerList.forEach(check304)
+
+export default {
+  name: 'Update Headers Upon a 304',
+  id: 'update304',
+  description: 'These tests check cache behaviour upon receiving a `304 Not Modified` response.',
+  spec_anchors: ['freshening.responses'],
+  tests
+}
diff --git a/test/fixtures/cache-tests/tests/updateHead.mjs b/test/fixtures/cache-tests/tests/updateHead.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/updateHead.mjs
@@ -0,0 +1,109 @@
+import * as templates from './lib/templates.mjs'
+
+export default
+
+{
+  name: 'HEAD updates',
+  id: 'updateHEAD',
+  description: 'These tests check how a cache updates stored responses when receiving a `HEAD` response.',
+  spec_anchors: ['head.effects'],
+  tests: [
+    {
+      name: 'Does HTTP cache write through a HEAD when stored response is stale?',
+      id: 'head-writethrough',
+      kind: 'check',
+      depends_on: ['freshness-max-age-stale'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          request_method: 'HEAD',
+          expected_method: 'HEAD'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache preserve stored fields not received in a `200` response to a `HEAD`?',
+      id: 'head-200-retain',
+      kind: 'check',
+      depends_on: ['head-writethrough'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          request_method: 'HEAD',
+          expected_method: 'HEAD',
+          expected_response_headers: [
+            ['Template-A', '1']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache update freshness lifetime recieved in a `200` response to a `HEAD`?',
+      id: 'head-200-freshness-update',
+      kind: 'check',
+      depends_on: ['head-writethrough'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          request_method: 'HEAD',
+          expected_method: 'HEAD',
+          response_headers: [
+            ['Cache-Control', 'max-age=1000']
+          ]
+        },
+        {
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache update stored fields recieved in a `200` response to a `HEAD`?',
+      id: 'head-200-update',
+      kind: 'check',
+      depends_on: ['head-200-freshness-update'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          request_method: 'HEAD',
+          expected_method: 'HEAD',
+          response_headers: [
+            ['Template-A', '2'],
+            ['Cache-Control', 'max-age=1000']
+          ]
+        },
+        {
+          expected_type: 'cached',
+          setup_tests: ['expected_type'],
+          expected_response_headers: [
+            ['Template-A', '2']
+          ]
+        }
+      ]
+    },
+    {
+      name: 'Does HTTP cache update stored fields recieved in a `410` response to a `HEAD`?',
+      id: 'head-410-update',
+      kind: 'check',
+      depends_on: ['head-200-freshness-update'],
+      requests: [
+        templates.becomeStale({}),
+        {
+          request_method: 'HEAD',
+          expected_method: 'HEAD',
+          response_status: [410, 'Gone'],
+          response_headers: [
+            ['Template-A', '2'],
+            ['Cache-Control', 'max-age=1000']
+          ]
+        },
+        {
+          expected_type: 'cached',
+          setup_tests: ['expected_type'],
+          expected_response_headers: [
+            ['Template-A', '2']
+          ]
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/vary-parse.mjs b/test/fixtures/cache-tests/tests/vary-parse.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/vary-parse.mjs
@@ -0,0 +1,157 @@
+import { makeTemplate } from './lib/templates.mjs'
+
+const varyParseSetup = makeTemplate({
+  request_headers: [
+    ['Foo', '1'],
+    ['Baz', '789']
+  ],
+  response_headers: [
+    ['Cache-Control', 'max-age=5000'],
+    ['Last-Modified', -3000],
+    ['Date', 0]
+  ],
+  setup: true
+})
+
+export default {
+  name: 'Vary Parsing',
+  id: 'vary-parse',
+  description: 'These tests check how caches parse the `Vary` response header.',
+  spec_anchors: ['caching.negotiated.responses'],
+  tests: [
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `*`',
+      id: 'vary-syntax-star',
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', '*', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `*, *`',
+      id: 'vary-syntax-star-star',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', '*, *', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `*, *` on different lines',
+      id: 'vary-syntax-star-star-lines',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', '*', false],
+            ['Vary', '*', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `, *`',
+      id: 'vary-syntax-empty-star',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', ', *', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `, *` on different lines',
+      id: 'vary-syntax-empty-star-lines',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', '', false],
+            ['Vary', '*', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `*, Foo`',
+      id: 'vary-syntax-star-foo',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', '*, Foo', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `Foo, *`',
+      id: 'vary-syntax-foo-star',
+      depends_on: ['freshness-max-age'],
+      requests: [
+        varyParseSetup({
+          response_headers: [
+            ['Vary', 'Foo, *', false]
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/fixtures/cache-tests/tests/vary.mjs b/test/fixtures/cache-tests/tests/vary.mjs
new file mode 100644
--- /dev/null
+++ b/test/fixtures/cache-tests/tests/vary.mjs
@@ -0,0 +1,470 @@
+import { makeTemplate } from './lib/templates.mjs'
+import * as utils from './lib/utils.mjs'
+
+const varySetup = makeTemplate({
+  request_headers: [
+    ['Foo', '1']
+  ],
+  response_headers: [
+    ['Cache-Control', 'max-age=5000'],
+    ['Last-Modified', -3000],
+    ['Date', 0],
+    ['Vary', 'Foo']
+  ],
+  setup: true
+})
+
+const vary2Setup = makeTemplate({
+  request_headers: [
+    ['Foo', '1'],
+    ['Bar', 'abc']
+  ],
+  response_headers: [
+    ['Cache-Control', 'max-age=5000'],
+    ['Last-Modified', -3000],
+    ['Date', 0],
+    ['Vary', 'Foo, Bar', false]
+  ],
+  setup: true
+})
+
+const vary3Setup = makeTemplate({
+  request_headers: [
+    ['Foo', '1'],
+    ['Bar', 'abc'],
+    ['Baz', '789']
+  ],
+  response_headers: [
+    ['Cache-Control', 'max-age=5000'],
+    ['Last-Modified', -3000],
+    ['Date', 0],
+    ['Vary', 'Foo, Bar, Baz', false]
+  ],
+  setup: true
+})
+
+export default {
+  name: 'Vary and Cache Keys',
+  id: 'vary',
+  description: 'These tests check how caches calculate a cache key using `Vary`.',
+  spec_anchors: ['caching.negotiated.responses'],
+  tests: [
+    {
+      name: 'An optimal HTTP cache reuses a `Vary` response when the request matches',
+      id: 'vary-match',
+      depends_on: ['freshness-max-age'],
+      kind: 'optimal',
+      requests: [
+        varySetup({}),
+        {
+          request_headers: [
+            ['Foo', '1']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: "HTTP cache must not reuse `Vary` response when request doesn't match",
+      id: 'vary-no-match',
+      depends_on: ['vary-match'],
+      requests: [
+        varySetup({}),
+        {
+          request_headers: [
+            ['Foo', '2']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response when stored request omits variant request header',
+      id: 'vary-omit-stored',
+      depends_on: ['vary-match'],
+      requests: [
+        {
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Foo']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Foo', '1']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response when presented request omits variant request header',
+      id: 'vary-omit',
+      depends_on: ['vary-match'],
+      requests: [
+        varySetup({}),
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache can store two different variants',
+      id: 'vary-invalidate',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        varySetup({
+          response_body: utils.httpContent('foo_1')
+        }),
+        {
+          request_headers: [
+            ['Foo', '2']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Foo']
+          ],
+          expected_type: 'not_cached',
+          response_body: utils.httpContent('foo_2'),
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Foo', '1']
+          ],
+          response_body: utils.httpContent('foo_1'),
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache should not include headers not listed in `Vary` in the cache key',
+      id: 'vary-cache-key',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        varySetup({
+          request_headers: [
+            ['Other', '2']
+          ]
+        }),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Other', '3']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a two-way `Vary` response when request matches',
+      id: 'vary-2-match',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        vary2Setup({}),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Bar', 'abc']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: "HTTP cache must not reuse two-way `Vary` response when request doesn't match",
+      id: 'vary-2-no-match',
+      depends_on: ['vary-2-match'],
+      requests: [
+        vary2Setup({}),
+        {
+          request_headers: [
+            ['Foo', '2'],
+            ['Bar', 'abc']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse two-way `Vary` response when request omits variant request header',
+      id: 'vary-2-match-omit',
+      depends_on: ['vary-2-match'],
+      requests: [
+        vary2Setup({}),
+        {
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a three-way `Vary` response when request matches',
+      id: 'vary-3-match',
+      depends_on: ['vary-2-match'],
+      kind: 'optimal',
+      requests: [
+        vary3Setup({}),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Bar', 'abc'],
+            ['Baz', '789']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: "HTTP cache must not reuse three-way `Vary` response when request doesn't match",
+      id: 'vary-3-no-match',
+      depends_on: ['vary-3-match'],
+      requests: [
+        vary3Setup({}),
+        {
+          request_headers: [
+            ['Foo', '2'],
+            ['Bar', 'abc'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: "HTTP cache must not reuse three-way `Vary` response when request doesn't match, regardless of header order",
+      id: 'vary-3-order',
+      depends_on: ['vary-3-match'],
+      requests: [
+        vary3Setup({}),
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789'],
+            ['Bar', 'abcde']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache reuses a three-way `Vary` response when both request and the original request omited a variant header',
+      id: 'vary-3-omit',
+      depends_on: ['vary-3-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Date', 0],
+            ['Last-Modified', -3000],
+            ['Vary', 'Foo, Bar, Baz', false] // FIXME: allow whitespace changes
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'HTTP cache must not reuse `Vary` response with a value of `*`',
+      id: 'vary-star',
+      requests: [
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', '*']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Baz', '789']
+          ],
+          expected_type: 'not_cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache normalises unknown selecting headers by combining fields',
+      id: 'vary-normalise-combine',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Foo', '1, 2']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Foo']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Foo', '1'],
+            ['Foo', '2']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache normalises `Accept-Language` by ignoring language order',
+      id: 'vary-normalise-lang-order',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Accept-Language', 'en, de']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Accept-Language']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Accept-Language', 'de, en']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache normalises `Accept-Language` by ignoring language case',
+      id: 'vary-normalise-lang-case',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Accept-Language', 'en, de']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Accept-Language']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Accept-Language', 'eN, De']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache normalises `Accept-Language` by ignoring whitespace',
+      id: 'vary-normalise-lang-space',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Accept-Language', 'en, de']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Accept-Language']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Accept-Language', ' en ,   de']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache selects `Content-Language` by using the qvalue on `Accept-Language`',
+      id: 'vary-normalise-lang-select',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Accept-Language', 'en, de']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Accept-Language'],
+            ['Content-Language', 'de']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Accept-Language', 'fr;q=0.5, de;q=1.0']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    },
+    {
+      name: 'An optimal HTTP cache normalises unknown selecting headers by removing whitespace',
+      id: 'vary-normalise-space',
+      depends_on: ['vary-match'],
+      kind: 'optimal',
+      requests: [
+        {
+          request_headers: [
+            ['Foo', '1,2']
+          ],
+          response_headers: [
+            ['Cache-Control', 'max-age=5000'],
+            ['Last-Modified', -3000],
+            ['Date', 0],
+            ['Vary', 'Foo']
+          ],
+          setup: true
+        },
+        {
+          request_headers: [
+            ['Foo', ' 1, 2 ']
+          ],
+          expected_type: 'cached'
+        }
+      ]
+    }
+  ]
+}
diff --git a/test/interceptors/cache.js b/test/interceptors/cache.js
--- a/test/interceptors/cache.js
+++ b/test/interceptors/cache.js
@@ -154,6 +154,73 @@ describe('Cache Interceptor', () => {
     strictEqual(await response.body.text(), 'asd')
   })
 
+  test('vary headers are present in revalidation request', async () => {
+    const clock = FakeTimers.install({
+      shouldClearNativeTimers: true
+    })
+
+    let requestsToOrigin = 0
+    let revalidationRequests = 0
+    const server = createServer((req, res) => {
+      res.setHeader('date', 0)
+      res.setHeader('cache-control', 's-maxage=1, stale-while-revalidate=10')
+
+      if (requestsToOrigin === 0) {
+        requestsToOrigin++
+        res.setHeader('vary', 'a, b')
+        res.setHeader('etag', '"asd"')
+        res.end('asd')
+      } else {
+        revalidationRequests++
+        notEqual(req.headers['if-none-match'], undefined)
+        notEqual(req.headers['a'], undefined)
+        notEqual(req.headers['b'], undefined)
+
+        res.statusCode = 304
+        res.end()
+      }
+    }).listen(0)
+
+    const client = new Client(`http://localhost:${server.address().port}`)
+      .compose(interceptors.cache())
+
+    after(async () => {
+      server.close()
+      await client.close()
+      clock.uninstall()
+    })
+
+    await once(server, 'listening')
+
+    strictEqual(requestsToOrigin, 0)
+    strictEqual(revalidationRequests, 0)
+
+    const request = {
+      origin: 'localhost',
+      path: '/',
+      method: 'GET',
+      headers: {
+        a: 'asd',
+        b: 'asd'
+      }
+    }
+
+    {
+      const response = await client.request(request)
+      strictEqual(requestsToOrigin, 1)
+      strictEqual(await response.body.text(), 'asd')
+    }
+
+    clock.tick(1500)
+
+    {
+      const response = await client.request(request)
+      strictEqual(requestsToOrigin, 1)
+      strictEqual(revalidationRequests, 1)
+      strictEqual(await response.body.text(), 'asd')
+    }
+  })
+
   test('revalidates request when needed', async () => {
     let requestsToOrigin = 0
 
@@ -162,6 +229,7 @@ describe('Cache Interceptor', () => {
     })
 
     const server = createServer((req, res) => {
+      res.setHeader('date', 0)
       res.setHeader('cache-control', 'public, s-maxage=1, stale-while-revalidate=10')
 
       requestsToOrigin++
@@ -228,6 +296,7 @@ describe('Cache Interceptor', () => {
     })
 
     const server = createServer((req, res) => {
+      res.setHeader('date', 0)
       res.setHeader('cache-control', 'public, s-maxage=1, stale-while-revalidate=10')
       requestsToOrigin++
 
@@ -373,8 +442,10 @@ describe('Cache Interceptor', () => {
   })
 
   test('necessary headers are stripped', async () => {
+    let requestsToOrigin = 0
     const server = createServer((req, res) => {
-      res.setHeader('cache-control', 'public, s-maxage=1, stale-while-revalidate=10, no-cache=should-be-stripped')
+      requestsToOrigin++
+      res.setHeader('cache-control', 'public, s-maxage=10, no-cache=should-be-stripped')
       res.setHeader('should-be-stripped', 'hello world')
       res.setHeader('should-not-be-stripped', 'dsa321')
 
@@ -398,8 +469,77 @@ describe('Cache Interceptor', () => {
     }
 
     // Send initial request. This should reach the origin
-    const response = await client.request(request)
-    strictEqual(await response.body.text(), 'asd')
+    {
+      const response = await client.request(request)
+      equal(requestsToOrigin, 1)
+      strictEqual(await response.body.text(), 'asd')
+      equal(response.headers['should-be-stripped'], 'hello world')
+      equal(response.headers['should-not-be-stripped'], 'dsa321')
+    }
+
+    // Send second request, this should hit the cache
+    {
+      const response = await client.request(request)
+      equal(requestsToOrigin, 1)
+      strictEqual(await response.body.text(), 'asd')
+      equal(response.headers['should-be-stripped'], undefined)
+      equal(response.headers['should-not-be-stripped'], 'dsa321')
+    }
+  })
+
+  test('necessary headers are stripped (quotes)', async () => {
+    let requestsToOrigin = 0
+    const server = createServer((_, res) => {
+      requestsToOrigin++
+      res.setHeader('connection', 'a, b')
+      res.setHeader('a', '123')
+      res.setHeader('b', '123')
+      res.setHeader('cache-control', 's-maxage=3600, no-cache="should-be-stripped, should-be-stripped2"')
+      res.setHeader('should-be-stripped', 'hello world')
+      res.setHeader('should-be-stripped2', 'hello world')
+      res.setHeader('should-not-be-stripped', 'dsa321')
+
+      res.end('asd')
+    }).listen(0)
+
+    const client = new Client(`http://localhost:${server.address().port}`)
+      .compose(interceptors.cache())
+
+    after(async () => {
+      server.close()
+      await client.close()
+    })
+
+    await once(server, 'listening')
+
+    const request = {
+      origin: 'localhost',
+      method: 'GET',
+      path: '/'
+    }
+
+    // Send initial request. This should reach the origin
+    {
+      const response = await client.request(request)
+      equal(requestsToOrigin, 1)
+      strictEqual(await response.body.text(), 'asd')
+      equal(response.headers['a'], '123')
+      equal(response.headers['b'], '123')
+      equal(response.headers['should-be-stripped'], 'hello world')
+      equal(response.headers['should-be-stripped2'], 'hello world')
+      equal(response.headers['should-not-be-stripped'], 'dsa321')
+    }
+
+    // Send second request, this should hit the cache
+    {
+      const response = await client.request(request)
+      equal(requestsToOrigin, 1)
+      strictEqual(await response.body.text(), 'asd')
+      equal(response.headers['a'], undefined)
+      equal(response.headers['b'], undefined)
+      equal(response.headers['should-be-stripped'], undefined)
+      equal(response.headers['should-be-stripped2'], undefined)
+    }
   })
 
   test('requests w/ unsafe methods never get cached', async () => {
@@ -441,6 +581,8 @@ describe('Cache Interceptor', () => {
       let requestsToOrigin = 0
       let revalidationRequests = 0
       const server = createServer((req, res) => {
+        res.setHeader('date', 0)
+
         if (req.headers['if-none-match']) {
           revalidationRequests++
           if (req.headers['if-none-match'] !== '"asd"') {
@@ -536,6 +678,8 @@ describe('Cache Interceptor', () => {
 
     let requestsToOrigin = 0
     const server = createServer((_, res) => {
+      res.setHeader('date', 0)
+
       requestsToOrigin++
       if (requestsToOrigin === 1) {
         // First request
@@ -615,6 +759,45 @@ describe('Cache Interceptor', () => {
     }
   })
 
+  test('cacheByDefault', async () => {
+    let requestsToOrigin = 0
+    const server = createServer((_, res) => {
+      requestsToOrigin++
+      res.end('asd')
+    }).listen(0)
+
+    after(() => server.close())
+
+    const client = new Client(`http://localhost:${server.address().port}`)
+      .compose(interceptors.cache({
+        cacheByDefault: 3600
+      }))
+
+    equal(requestsToOrigin, 0)
+
+    // Should hit the origin
+    {
+      const res = await client.request({
+        origin: 'localhost',
+        path: '/',
+        method: 'GET'
+      })
+      equal(requestsToOrigin, 1)
+      equal(await res.body.text(), 'asd')
+    }
+
+    // Should hit the cache
+    {
+      const res = await client.request({
+        origin: 'localhost',
+        path: '/',
+        method: 'GET'
+      })
+      equal(requestsToOrigin, 1)
+      equal(await res.body.text(), 'asd')
+    }
+  })
+
   describe('Client-side directives', () => {
     test('max-age', async () => {
       const clock = FakeTimers.install({
@@ -696,6 +879,7 @@ describe('Cache Interceptor', () => {
       })
 
       const server = createServer((req, res) => {
+        res.setHeader('date', 0)
         res.setHeader('cache-control', 'public, s-maxage=1, stale-while-revalidate=10')
 
         if (requestsToOrigin === 1) {
@@ -768,6 +952,7 @@ describe('Cache Interceptor', () => {
 
       const server = createServer((req, res) => {
         requestsToOrigin++
+        res.setHeader('date', 0)
         res.setHeader('cache-control', 'public, s-maxage=10')
         res.end('asd')
       }).listen(0)
@@ -988,6 +1173,8 @@ describe('Cache Interceptor', () => {
 
       let requestsToOrigin = 0
       const server = createServer((_, res) => {
+        res.setHeader('date', 0)
+
         requestsToOrigin++
         if (requestsToOrigin === 1) {
           // First request, send stale-while-revalidate to keep the value in the cache