Repository: whatwg/fetch

Issue #883: Handling of invalid Location header characters doesn't match browsers
Created: 2019-03-19T21:22:31Z
We recently regressed some handling of invalid Location headers in Chrome. In digging into that, I noticed the Fetch spec doesn't really match browsers here. I should also note I'm not very familiar with the structure of these specifications, so it's certainly possible I'm reading some of them wrong.
Fetch appears to define handling of the Location header as (https://fetch.spec.whatwg.org/#http-fetch, steps 5.2 and 5.3):

Let location be the result of extracting header list values given Location and actualResponse’s header list.
If location is a value, then set location to the result of parsing location with actualResponse’s URL.

Note, in particular, that "extracting header list values" is defined in terms of the headers ABNF. I don't see a Location ABNF in Fetch, so I assume this is intended to be the one in RFC 7231. That cites various URI-related ABNFs in RFC 3986, which implies only a subset of ASCII as allowed, and instead other things should have already been percent-encoded. Inputs that don't match appear to be an error ("extract header values" returns an error when the ABNF does not match). Is that the intended reading?
This doesn't appear to match what is actually implemented by browsers or necessary for web compatibility. In particular, we had a report that users editing ru.wikipedia.org, until recently, would be served redirects where Cyrillic characters in the path were escaped, but those in the fragment were not.
Some interesting cases to test:
Note: when testing Chrome, test with Chrome canary, specifically 75.0.3735.0 or later. 73 has a bug. 74 should have the fix in not too long but currently does not.
\xff refers to a byte with value 0xff. %ff refers to three bytes with values %, f, and f.

Location: https://example.com/#\xe2\x98\x83 (some unescaped UTF-8 character)
Location: https://example.com/#\xe2\x98\x83%e2%98%83 (mix of escaped and unescaped)
Location: https://example.com/#%\xe2\x98\x83 (something after a %)
Location: https://example.com/#\xff (UTF-8 encoding error)

Chrome (pre-73 and 75+), Firefox, and Safari all appear to resolve these to:

https://example.com/#%E2%98%83
https://example.com/#%E2%98%83%e2%98%83
https://example.com/#%%E2%98%83
https://example.com/#%FF

(I haven't tested Edge and IE.)
That last case is particularly interesting. Chrome internally percent-encodes all non-ASCII header bytes before passing the string on to URL handling logic. (This logic is very very old, so I don't know what originally motivated it.)
This seems a goofy way to specify it, but the obvious alternative doesn't seem to work. Suppose Fetch just said all Location headers were valid and you just pass the value to the URL parsing algorithm, I don't think that's what would come out. The URL parsing algorithm takes a string input, not bytes, and seems to primarily percent-encode as UTF-8. Interestingly, in the quoted text above, Fetch passes a value to that function, which is a byte sequence, not a string. And, indeed if I remove the funny preprocessing step in Chrome, %FF turns into %EF%BF%BD (U+FFFD).

Duplicate issue #1720: Clarity of Location URL Algorithm
Created: 2023-10-29T17:17:50Z
What is the issue with the Fetch Standard?
Under Fetch Spec - Response Location URL Step 3, there is no clear indication for what happens if extracting the header list values gives multiple Location headers.
Say, the response has the following headers:
Location: https://www.example.com/
Location: https://www.example.net/
Although such a response is malformed, it should still be addressed explicitly.
Presumably, the algorithm should return failure in this scenario.

Closing comment (2023-10-29T18:32:23Z): That's correct, if you're willing to help writing tests for this that'd be appreciated. This is tracked by #883 so resolving this as a duplicate.