Open Bug 1809403 Opened 2 years ago Updated 2 years ago

Double RFC9218 HTTP Priority header field when set via fetch()

Categories

(Core :: DOM: Networking, defect, P3)

Firefox 108
defect

Tracking

()

People

(Reporter: marx.robin, Unassigned)

References

(Blocks 1 open bug)

Details

(Whiteboard: [necko-triaged])

Steps to reproduce:

  1. Initiate a fetch() request, manually setting the RFC 9218 Priority request header field. For example:
      fetch('assets/data.json', { headers: {
        'priority': 'u=5,i'
      }})
      .then((response) => response.text())
      .then((data) => console.log("result : " + data));

Or with an invalid/illegal value for (part of) the field (max for u is 7):

      fetch('assets/data.json', { headers: {
        'priority': 'u=22,i'
      }})
      .then((response) => response.text())
      .then((data) => console.log("result : " + data));

Actual results:

Firefox sends the Priority header field to the server twice:

  1. with its default value for a fetch() call, which is u=4
  2. with the user-provided value in the fetch(), even if the value is (partially) invalid

Expected results:

Sending the same field with conflicting values seems prohibited by RFC9110 (https://www.rfc-editor.org/rfc/rfc9110.html#name-field-order). Firefox should use one of the values and not send the other.

In comparison: Safari chooses the user-provided value and doesn't send its original value. Chromium sends the user-provided value, but also sends its original value in a PRIORITY_UPDATE frame (Chromium currently does not use the header field, but this will soon change). More details here: https://calendar.perfplanet.com/2022/http-3-prioritization-demystified/

If the choice is made to keep the user-provided value, it might be interesting to validate/clamp the data. For example, the value for u is defined between 0 and 7, and so u=22 might be changed to u=7 instead. Alternatively, it could be clamped to u=6 so the browser keeps control over the outer values for overrides. This is especially interesting for u=0, which is the highest priority (u=-3 could become u=1 instead).

In this case, a decision must be made on how to handle unknown parameters in the field value. Currently, RFC9218 only defines 2 (u and i), but the scheme is intended to be extensible in the future. As such, discarding unknown parameters may or may not be ideal for experimentation and future robustness.

This was discussed partially with Martin Thomson via the quicdev slack in January 2023.

The Bugbug bot thinks this bug should belong to the 'Core::DOM: Networking' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.

Component: Untriaged → DOM: Networking
Product: Firefox → Core

We should let the value from the fetch API stand, unless it is directly invalid. RFC 8941 suggests that invalid values - according to its rules - are dropped.

The change here is to parse the field value, as provided, and accept the value if it parses correctly, preserving any values we don't understand (so that we aren't the source of ossification). Then, if a value is present for "u", we should not add one (we don't add "i" for fetch, nor should we, but we should retain it if present).

Note that as an sf dictionary, keys can have parameters instead of a value. Again, we should drop these only if they are known to us and obviously invalid ("u" and "i"). As the spec requires:

Where the Dictionary is successfully parsed, this document places the additional requirement that unknown priority parameters, priority parameters with out-of-range values, or values of unexpected types MUST be ignored.

That is, we can drop them only if we know that the server will drop them.

Status: UNCONFIRMED → NEW
Type: enhancement → defect
Ever confirmed: true
OS: Unspecified → All
Hardware: Unspecified → All

Oh, this is probably not strictly a defect. RFC 9110 doesn't prohibit repetitions of fields or field values (field semantics are per-field and this could be valid; in some cases it is). RFC 9218 appears to be silent with respect to how to treat repeated keys (with the same or different value, it matters not). Either way, in this case, it creates a conflict unnecessarily so we shouldn't add a conflict (or repetition).

My intention was always to let RFC 9218 defer to Structured Fields (RFC 8941 at time of writing) on this matter. For instance, see https://httpwg.org/specs/rfc8941.html#dictionary and in particular

Note that Dictionaries can have their members split across multiple lines of the same header or trailer section; for example, the following are equivalent:

Example-Dict: foo=1, bar=2
and
Example-Dict: foo=1
Example-Dict: bar=2

and https://httpwg.org/specs/rfc8941.html#parse-dictionary step 4

  1. If dictionary already contains a key this_key (comparing character for character), overwrite its value with member.

I agree the user agent is creating or repetition. It would be nice if that was avoided. A mature recipient should handle such a case but this current behavior might catch out naive ones.

Blocks: fetch
Severity: -- → S3
Priority: -- → P3
Whiteboard: [necko-triaged]
You need to log in before you can comment on or make changes to this bug.