From 7645fb75caefefb613b084a9ee4f11bbecc4df6f Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Tue, 22 Oct 2024 11:14:00 -0400 Subject: [PATCH] client-side redirect, remove bikeshed file --- .gitignore | 1 - Makefile | 5 +- no-vary-search.bs | 353 -------------------------------------------- no-vary-search.html | 5 + 4 files changed, 8 insertions(+), 356 deletions(-) delete mode 100644 no-vary-search.bs create mode 100644 no-vary-search.html diff --git a/.gitignore b/.gitignore index ae7700d..deb66c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -no-vary-search.html prefetch.html prerendering.html speculation-rules.html diff --git a/Makefile b/Makefile index e9ab07b..a22ba79 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ SHELL=/bin/bash -bikeshed_files = no-vary-search.bs prefetch.bs prerendering.bs speculation-rules.bs +bikeshed_files = prefetch.bs prerendering.bs speculation-rules.bs +html_files = index.html no-vary-search.html .PHONY: ci clean local remote @@ -9,7 +10,7 @@ local: $(bikeshed_files) remote: $(bikeshed_files:.bs=.html) -ci: index.html $(bikeshed_files:.bs=.html) +ci: $(html_files) $(bikeshed_files:.bs=.html) mkdir -p out cp $^ out/ diff --git a/no-vary-search.bs b/no-vary-search.bs deleted file mode 100644 index 856a150..0000000 --- a/no-vary-search.bs +++ /dev/null @@ -1,353 +0,0 @@ -
-Title: No-Vary-Search
-Shortname: no-vary-search
-Group: WICG
-Status: CG-DRAFT
-Repository: WICG/nav-speculation
-URL: https://wicg.github.io/nav-speculation/no-vary-search.html
-Level: 1
-Editor: Domenic Denicola, Google https://www.google.com/, d@domenic.me
-Abstract: A proposed HTTP header field for changing how URL search parameters impact caching
-Markup Shorthands: css no, markdown yes
-Assume Explicit For: yes
-Complain About: accidental-2119 yes, missing-example-ids yes
-Indent: 2
-Boilerplate: omit conformance
-
-
-spec: RFC8941; urlPrefix: https://www.rfc-editor.org/rfc/rfc8941.html
-  type: dfn
-    text: structured header; url: #section-1
-    for: structured header
-      text: dictionary; url: name-dictionaries
-      text: boolean; url: name-boolean
-      text: inner list; url: name-inner-lists
-    text: parsing structured fields; url: #text-parse
-
- - -

Status and venue note

- -Work on this document has moved to the IETF HTTP working group. The latest draft and issue tracker can be found there. - -

HTTP header field definition

- -The \`No-Vary-Search\` HTTP header field is a [=structured header=] whose value must be a [=structured header/dictionary=]. - -TODO: probably give some more introductory non-normative text. Look at what other HTTP field defintions do. - -It has the following authoring conformance requirements: - -* If present, the `key-order` entry's value must be a [=structured header/boolean=]. -* If present, the `params` entry's value must be either a [=structured header/boolean=] or an [=structured header/inner list=]. -* If present, the `except` entry's value must be a [=structured header/inner list=]. -* The `except` entry must only be present if the `params` entry is also present, and the `params` entry's value is the boolean value true. - -The dictionary may contain entries whose keys are not one of `key-order`, `params`, and `except`, but their meaning is not defined by this specification. Implementations of this specification will ignore such entries (but future documents may assign meaning to such entries). - -

As always, the authoring conformance requirements are not binding on implementations. Implementations instead need to implement the processing model given by the [=obtain a URL search variance=] algorithm. - -

Data model

- -A URL search variance is a [=struct=] whose [=struct/items=] are the following: - -* no-vary params, either the special value wildcard or a [=list=] of [=strings=] -* vary params, either the special value wildcard or a [=list=] of [=strings=] -* vary on key order, a [=boolean=] - -The default URL search variance is a [=URL search variance=] whose [=URL search variance/no-vary params=] is an empty list, [=URL search variance/vary params=] is [=URL search variance/vary params/wildcard=], and [=URL search variance/vary on key order=] is true. - -The [=obtain a URL search variance=] algorithm ensures that all [=URL search variances=] obey the following constraints: - -* [=URL search variance/vary params=] is a [=list=] if and only if the [=URL search variance/no-vary params=] is [=URL search variance/no-vary params/wildcard=]; and -* [=URL search variance/no-vary params=] is a [=list=] if and only if the [=URL search variance/vary params=] is [=URL search variance/vary params/wildcard=]. - -

Parsing

- -
- To parse a URL search variance given a [=map=] |value|: - - 1. If |value| is null, then return the [=default URL search variance=]. - 1. If |value|'s [=map/keys=] [=list/contains=] anything other than "`key-order`", "`params`", or "`except`", then return the [=default URL search variance=]. - 1. Let |result| be a new [=URL search variance=]. - 1. Set |result|'s [=URL search variance/vary on key order=] to true. - 1. If |value|["`key-order`"] [=map/exists=]: - 1. If |value|["`key-order`"] is not a [=boolean=], then return the [=default URL search variance=]. - 1. Set |result|'s [=URL search variance/vary on key order=] to the boolean negation of |value|["`key-order`"]. - 1. If |value|["`params`"] [=map/exists=]: - 1. If |value|["`params`"] is a [=boolean=]: - 1. If |value|["`params`"] is true, then: - 1. Set |result|'s [=URL search variance/no-vary params=] to [=URL search variance/no-vary params/wildcard=]. - 1. Set |result|'s [=URL search variance/vary params=] to the empty list. - 1. Otherwise: - 1. Set |result|'s [=URL search variance/no-vary params=] to the empty list. - 1. Set |result|'s [=URL search variance/vary params=] to [=URL search variance/no-vary params/wildcard=]. - 1. Otherwise, if |value|["`params`"] is a [=list=]: - 1. If any [=list/item=] in |value|["`params`"] is not a [=string=], then return the [=default URL search variance=]. - 1. Set |result|'s [=URL search variance/no-vary params=] to the result of applying [=parse a key=] to each [=list/item=] in |value|["`params`"]. - 1. Set |result|'s [=URL search variance/vary params=] to [=URL search variance/no-vary params/wildcard=]. - 1. Otherwise, return the [=default URL search variance=]. - 1. If |value|["`except`"] [=map/exists=]: - 1. If |value|["`params`"] is not true, then return the [=default URL search variance=]. - 1. If |value|["`except`"] is not a [=list=], then return the [=default URL search variance=]. - 1. If any [=list/item=] in |value|["`except`"] is not a [=string=], then return the [=default URL search variance=]. - 1. Set |result|'s [=URL search variance/vary params=] to the result of applying [=parse a key=] to each [=list/item=] in |value|["`except`"]. - 1. Return |result|. - -
-

In general, this algorithm is strict and tends to return the [=default URL search variance=] whenever it sees something it doesn't recognize. This is because the [=default URL search variance=] behavior will just cause fewer cache hits, which is an acceptable fallback behavior.

- -

However, unrecognized keys at the top level are ignored, to make it easier to extend this specification in the future. To avoid misbehavior with existing client software, such extensions will likely expand, rather than reduce, the set of requests that a cached response can match.

-
-
- -
- To obtain a URL search variance given a [=response=] |response|: - - 1. Let |fieldValue| be the result of [=header list/getting a structured field value=] given [:No-Vary-Search:] and "`dictionary`" from |response|'s [=response/header list=]. - 1. Return the result of [=parsing a URL search variance=] given |fieldValue|. - -
- -
- The following illustrates how various inputs are parsed, in terms of their impacting on the resulting [=URL search variance/no-vary params=] and [=URL search variance/vary params=]: - - - - - - - - - - -
InputResult
No-Vary-Search: params
-
- * [=URL search variance/no-vary params=]: [=URL search variance/no-vary params/wildcard=] - * [=URL search variance/vary params=]: (empty list) -
No-Vary-Search: params=("a")
-
- * [=URL search variance/no-vary params=]: « "`a`" » - * [=URL search variance/vary params=]: [=URL search variance/vary params/wildcard=] -
No-Vary-Search: params, except=("x")
-
- * [=URL search variance/no-vary params=]: [=URL search variance/no-vary params/wildcard=] - * [=URL search variance/vary params=]: « "`x`" » -
-
- -
- The following inputs are all invalid and will cause the [=default URL search variance=] to be returned: - - * `No-Vary-Search: unknown-key` - * `No-Vary-Search: key-order="not a boolean"` - * `No-Vary-Search: params="not a boolean or inner list"` - * `No-Vary-Search: params=(not-a-string)` - * `No-Vary-Search: params=("a"), except=("x")` - * `No-Vary-Search: params=(), except=()` - * `No-Vary-Search: params=?0, except=("x")` - * `No-Vary-Search: params, except=(not-a-string)` - * `No-Vary-Search: params, except="not an inner list"` - * `No-Vary-Search: params, except=?1` - * `No-Vary-Search: except=("x")` - * `No-Vary-Search: except=()` -
- -
- The following inputs are valid, but somewhat unconventional. They are shown alongside their more conventional form. - - - - - - - - - - - -
Input - Conventional form -
No-Vary-Search: params=?1
-
No-Vary-Search: params
-
No-Vary-Search: key-order=?1
-
No-Vary-Search: key-order
-
No-Vary-Search: params, key-order, except=("x")
-
No-Vary-Search: key-order, params, except=("x")
-
No-Vary-Search: params=?0
-
(omit the header) -
No-Vary-Search: params=()
-
(omit the header) -
No-Vary-Search: key-order=?0
-
(omit the header) -
-
- -
- To obtain a URL search variance hint given a [=string=] |hintValue|: - - 1. Let |fieldValue| be the result of [=parsing structured fields=] given |hintValue| and "`dictionary`". - 1. If parsing failed, then return the [=default URL search variance=]. - 1. Return the result of [=parsing a URL search variance=] given |fieldValue|. - -
- -
- To parse a key given an [=ASCII string=] |keyString|: - - 1. Let |keyBytes| be the [=isomorphic encoding=] of |keyString|. - - 1. Replace any 0x2B (+) in |keyBytes| with 0x20 (SP). - - 1. Let |keyBytesDecoded| be the [=byte sequence/percent-decoding=] of |keyBytes|. - - 1. Let |keyStringDecoded| be the [=UTF-8 decode without BOM|UTF-8 decoding without BOM=] of |keyBytesDecoded|. - - 1. Return |keyStringDecoded|. -
- -
- The [=parse a key=] algorithm allows encoding non-ASCII key strings in the ASCII structured header format, similar to how the application/x-www-form-urlencoded format allows encoding an entire entry list of keys and values in ASCII URL format. For example, - -
No-Vary-Search: params=("%C3%A9+%E6%B0%97")
- - will result in a [=URL search variance=] whose [=URL search variance/vary params=] are « "`é 気`" ». As explained in a later example, the canonicalization process during [=equivalent modulo search variance|equivalence testing=] means this will treat as equivalent URL strings such as: - - * `https://example.com/?é 気=1` - * `https://example.com/?é+気=2` - * `https://example.com/?%C3%A9%20気=3` - * `https://example.com/?%C3%A9+%E6%B0%97=4` - - and so on, since they all are [=urlencoded parser|parsed=] to having the same key "`é 気`". -
- -

Comparing

- -Two [=URLs=] |urlA| and |urlB| are equivalent modulo search variance given a [=URL search variance=] |searchVariance| if the following algorithm returns true: - -1. If the [=url/scheme=], [=url/username=], [=url/password=], [=url/host=], [=url/port=], or [=url/path=] of |urlA| and |urlB| differ, then return false. - -1. If |searchVariance| is equivalent to the [=default URL search variance=], then: - - 1. If |urlA|'s [=url/query=] equals |urlB|'s [=url/query=], then return true. - - 1. Return false. - -

In this case, even [=URL=] pairs that might appear the same after running the [=urlencoded parser|application/x-www-form-urlencoded parser=] on their [=url/queries=], such as `https://example.com/a` and `https://example.com/a?`, or `https://example.com/foo?a=b&&&c` and `https://example.com/foo?a=b&c=`, will be treated as inequivalent. - -1. Let |searchParamsA| and |searchParamsB| be empty [=lists=]. - -1. If |urlA|'s [=url/query=] is not null, then set |searchParamsA| to the result of running the [=urlencoded parser|application/x-www-form-urlencoded parser=] given the [=isomorphic encoding=] of |urlA|'s [=url/query=]. - -1. If |urlB|'s [=url/query=] is not null, then set |searchParamsB| to the result of running the [=urlencoded parser|application/x-www-form-urlencoded parser=] given the [=isomorphic encoding=] of |urlB|'s [=url/query=]. - -1. If |searchVariance|'s [=URL search variance/no-vary params=] is a [=list=], then: - - 1. Set |searchParamsA| to a [=list=] containing those [=list/items=] |pair| in |searchParamsA| where |searchVariance|'s [=URL search variance/no-vary params=] does not [=list/contain=] |pair|[0]. - - 1. Set |searchParamsB| to a [=list=] containing those [=list/items=] |pair| in |searchParamsB| where |searchVariance|'s [=URL search variance/no-vary params=] does not [=list/contain=] |pair|[0]. - -1. Otherwise, if |searchVariance|'s [=URL search variance/vary params=] is a [=list=], then: - - 1. Set |searchParamsA| to a [=list=] containing those [=list/items=] |pair| in |searchParamsA| where |searchVariance|'s [=URL search variance/vary params=] [=list/contains=] |pair|[0]. - - 1. Set |searchParamsB| to a [=list=] containing those [=list/items=] |pair| in |searchParamsB| where |searchVariance|'s [=URL search variance/vary params=] [=list/contains=] |pair|[0]. - -1. If |searchVariance|'s [=URL search variance/vary on key order=] is false, then: - - 1. Let |keyLessThan| be an algorithm taking as inputs two pairs (|keyA|, valueA) and (|keyB|, valueB), which returns whether |keyA| is [=code unit less than=] |keyB|. - - 1. Set |searchParamsA| to the result of [=list/sorting in ascending order=] |searchParamsA|, with |keyLessThan|. - - 1. Set |searchParamsB| to the result of [=list/sorting in ascending order=] |searchParamsB|, with |keyLessThan|. - -1. If |searchParamsA|'s [=list/size=] is not equal to |searchParamsB|'s [=list/size=], then return false. - -1. Let |i| be 0. - -1. [=iteration/While=] |i| < |searchParamsA|'s [=list/size=]: - - 1. If |searchParamsA|[|i|][0] does not equal |searchParamsB|[|i|][0], then return false. - - 1. If |searchParamsA|[|i|][1] does not equal |searchParamsB|[|i|][1], then return false. - - 1. Set |i| to |i| + 1. - -1. Return true. - -

- Due to how the [=urlencoded parser|application/x-www-form-urlencoded parser=] canonicalizes query strings, there are some cases where query strings which do not appear obviously equivalent, will end up being treated as equivalent after parsing. - - So, for example, given any non-default value for `No-Vary-Search`, such as `No-Vary-Search: key-order`, we will have the following equivalences: - - - - - - - - - - - - - - - - - - - - -
Equivalent URL strings - Explanation -
`https://example.com/` - A null [=url/query=] is parsed the same as an empty string query -
`https://example.com/?` -
`https://example.com/?a=x` - Parsing performs percent-decoding -
`https://example.com/?%61=%78` -
`https://example.com/?a=é` - Parsing performs percent-decoding -
`https://example.com/?a=%C3%A9` -
`https://example.com/?a=%f6` - Both values are parsed as U+FFFD (�) -
`https://example.com/?a=%ef%bf%bd` -
`https://example.com/?a=x&&&&` - Parsing splits on `&` and discards empty strings -
`https://example.com/?a=x` -
`https://example.com/?a=` - Both parse as having an empty string value for `a` -
`https://example.com/?a` -
`https://example.com/?a=%20` - `+` and `%20` are both parsed as U+0020 SPACE -
`https://example.com/?a=+` -
`https://example.com/?a= &` -
-
- -

Security considerations

-The main risk to be aware of is the impact of mismatched URLs. In particular, this could cause the user to see a response that was originally fetched from a URL different from the one displayed when they hovered a link, or the URL displayed in the URL bar. - -However, since the impact is limited to query parameters, this does not cross the relevant security boundary, which is the [=origin=]. (Or perhaps just the [=url/host=], from [[URL#url-rendering-simplification|the perspective of security UI]].) Indeed, we have already given origins complete control over how they present the (URL, reponse body) pair, including on the client side via technology such as {{History/replaceState()|history.replaceState()}} or service workers. - -

Privacy considerations

- -This proposal is adjacent to the highly-privacy-relevant space of [[NAV-TRACKING-MITIGATIONS#terminology|navigational tracking]], which often uses query parameters to pass along user identifiers. However, we believe this proposal itself does not have privacy impacts. It does not interfere with [[NAV-TRACKING-MITIGATIONS#deployed-mitigations|existing navigational tracking mitigations]], or any known future ones being contemplated. Indeed, if a page were to encode user identifiers in its URL, the only ability this proposal gives is to *reduce* such user tracking by preventing server processing of such user IDs (since the server is bypassed in favor of the cache). diff --git a/no-vary-search.html b/no-vary-search.html new file mode 100644 index 0000000..1c7e25e --- /dev/null +++ b/no-vary-search.html @@ -0,0 +1,5 @@ + +Moved to ITEF + +

Moved to IETF

+

This work has moved to the IETF HTTP working group. You will be redirected to the latest revision shortly.