Skip to content

Commit 92e6c91

Browse files
authored
Refactor forbidden request-headers
This change makes several changes: * Makes "get, decode, and split" callable with a value. This is generally not advisable so this is not exported. * Generalizes forbidden header name to forbidden request-headers so it can target values for specific headers. * Takes advantage of that for X-HTTP-Method, X-HTTP-Method-Override, and X-Method-Override. Tests: WPT fetch/api/basic/request-forbidden-headers.any.js. XHR PR: whatwg/xhr#362.
1 parent b482186 commit 92e6c91

File tree

1 file changed

+148
-108
lines changed

1 file changed

+148
-108
lines changed

fetch.bs

Lines changed: 148 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -621,70 +621,18 @@ a <a for=/>header name</a> <var>name</var> from <a for=/>header list</a> <var>li
621621
steps:
622622

623623
<ol>
624-
<li><p>Let <var>initialValue</var> be the result of <a for="header list">getting</a>
625-
<var>name</var> from <var>list</var>.
626-
627-
<li><p>If <var>initialValue</var> is null, then return null.
628-
629-
<li><p>Let <var>input</var> be the result of <a>isomorphic decoding</a> <var>initialValue</var>.
630-
631-
<li><p>Let <var>position</var> be a <a for=string>position variable</a> for <var>input</var>,
632-
initially pointing at the start of <var>input</var>.
633-
634-
<li><p>Let <var>values</var> be a <a for=/>list</a> of <a for=/>strings</a>, initially empty.
635-
636-
<li><p>Let <var>value</var> be the empty string.
637-
638-
<li>
639-
<p>While <var>position</var> is not past the end of <var>input</var>:
640-
641-
<ol>
642-
<li>
643-
<p>Append the result of <a>collecting a sequence of code points</a> that are not U+0022 (") or
644-
U+002C (,) from <var>input</var>, given <var>position</var>, to <var>value</var>.
645-
646-
<p class=note>The result might be the empty string.
647-
648-
<li>
649-
<p>If <var>position</var> is not past the end of <var>input</var>, then:
650-
651-
<ol>
652-
<li>
653-
<p>If the <a for=/>code point</a> at <var>position</var> within <var>input</var> is
654-
U+0022 ("), then:
655-
656-
<ol>
657-
<li><p>Append the result of <a>collecting an HTTP quoted string</a> from <var>input</var>,
658-
given <var>position</var>, to <var>value</var>.
659-
660-
<li>If <var>position</var> is not past the end of <var>input</var>, then
661-
<a for=iteration>continue</a>.
662-
</ol>
663-
664-
<li>
665-
<p>Otherwise:
666-
667-
<ol>
668-
<li><p>Assert: the <a for=/>code point</a> at <var>position</var> within <var>input</var> is
669-
U+002C (,).
670-
671-
<li><p>Advance <var>position</var> by 1.
672-
</ol>
673-
</ol>
674-
675-
<li><p>Remove all <a>HTTP tab or space</a> from the start and end of <var>value</var>.
676-
677-
<li><p><a for=list>Append</a> <var>value</var> to <var>values</var>.
624+
<li><p>Let <var>value</var> be the result of <a for="header list">getting</a> <var>name</var> from
625+
<var>list</var>.
678626

679-
<li><p>Set <var>value</var> to the empty string.
680-
</ol>
627+
<li><p>If <var>value</var> is null, then return null.
681628

682-
<li><p>Return <var>values</var>.
629+
<li><p>Return the result of <a for="header value">getting, decoding, and splitting</a>
630+
<var>value</var>.
683631
</ol>
684632

685633
<div class=example id=example-header-list-get-decode-split>
686-
<p>This is how <a>get, decode, and split</a> functions in practice with `<code>A</code>` as the
687-
<var>name</var> argument:
634+
<p>This is how <a for="header list">get, decode, and split</a> functions in practice with
635+
`<code>A</code>` as the <var>name</var> argument:
688636

689637
<table>
690638
<tr>
@@ -755,6 +703,70 @@ A: 3
755703
</table>
756704
</div>
757705

706+
<p>To
707+
<dfn for="header value" lt="get, decode, and split|getting, decoding, and splitting">get, decode, and split</dfn>
708+
a <a for=/>header value</a> <var>value</var>, run these steps:
709+
710+
<ol>
711+
<li><p>Let <var>input</var> be the result of <a>isomorphic decoding</a> <var>value</var>.
712+
713+
<li><p>Let <var>position</var> be a <a for=string>position variable</a> for <var>input</var>,
714+
initially pointing at the start of <var>input</var>.
715+
716+
<li><p>Let <var>values</var> be a <a for=/>list</a> of <a for=/>strings</a>, initially empty.
717+
718+
<li><p>Let <var>temporaryValue</var> be the empty string.
719+
720+
<li>
721+
<p>While <var>position</var> is not past the end of <var>input</var>:
722+
723+
<ol>
724+
<li>
725+
<p>Append the result of <a>collecting a sequence of code points</a> that are not U+0022 (") or
726+
U+002C (,) from <var>input</var>, given <var>position</var>, to <var>temporaryValue</var>.
727+
728+
<p class=note>The result might be the empty string.
729+
730+
<li>
731+
<p>If <var>position</var> is not past the end of <var>input</var>, then:
732+
733+
<ol>
734+
<li>
735+
<p>If the <a for=/>code point</a> at <var>position</var> within <var>input</var> is
736+
U+0022 ("), then:
737+
738+
<ol>
739+
<li><p>Append the result of <a>collecting an HTTP quoted string</a> from <var>input</var>,
740+
given <var>position</var>, to <var>temporaryValue</var>.
741+
742+
<li>If <var>position</var> is not past the end of <var>input</var>, then
743+
<a for=iteration>continue</a>.
744+
</ol>
745+
746+
<li>
747+
<p>Otherwise:
748+
749+
<ol>
750+
<li><p>Assert: the <a for=/>code point</a> at <var>position</var> within <var>input</var> is
751+
U+002C (,).
752+
753+
<li><p>Advance <var>position</var> by 1.
754+
</ol>
755+
</ol>
756+
757+
<li><p>Remove all <a>HTTP tab or space</a> from the start and end of <var>temporaryValue</var>.
758+
759+
<li><p><a for=list>Append</a> <var>temporaryValue</var> to <var>values</var>.
760+
761+
<li><p>Set <var>temporaryValue</var> to the empty string.
762+
</ol>
763+
764+
<li><p>Return <var>values</var>.
765+
</ol>
766+
767+
<p class=note>Except for blessed call sites, the algorithm directly above is not to be invoked
768+
directly. Use <a for="header list">get, decode, and split</a> instead.
769+
758770
<p>To <dfn export for="header list" id=concept-header-list-append>append</dfn> a <a for=/>header</a>
759771
(<var>name</var>, <var>value</var>) to a <a for=/>header list</a> <var>list</var>, run these steps:
760772

@@ -1054,35 +1066,63 @@ is a <a>byte-case-insensitive</a> match for one of
10541066
<a>CORS-safelisted request-header</a>.
10551067
</ol>
10561068

1057-
<p>A <dfn export>forbidden header name</dfn> is a <a for=/>header name</a> that is a
1058-
<a>byte-case-insensitive</a> match for one of
1069+
<p id=forbidden-header-name>A <a for=/>header</a> (<var>name</var>, <var>value</var>) is
1070+
<dfn export>forbidden request-header</dfn> if these steps return true:
10591071

1060-
<ul class=brief>
1061-
<li>`<code>Accept-Charset</code>`
1062-
<li>`<code>Accept-Encoding</code>`
1063-
<li>`<a http-header><code>Access-Control-Request-Headers</code></a>`
1064-
<li>`<a http-header><code>Access-Control-Request-Method</code></a>`
1065-
<li>`<code>Connection</code>`
1066-
<li>`<code>Content-Length</code>`
1067-
<li>`<code>Cookie</code>`
1068-
<li>`<code>Cookie2</code>`
1069-
<li>`<code>Date</code>`
1070-
<li>`<code>DNT</code>`
1071-
<li>`<code>Expect</code>`
1072-
<li>`<code>Host</code>`
1073-
<li>`<code>Keep-Alive</code>`
1074-
<li>`<a http-header><code>Origin</code></a>`
1075-
<li>`<code>Referer</code>`
1076-
<li>`<code>Set-Cookie</code>`
1077-
<li>`<code>TE</code>`
1078-
<li>`<code>Trailer</code>`
1079-
<li>`<code>Transfer-Encoding</code>`
1080-
<li>`<code>Upgrade</code>`
1081-
<li>`<code>Via</code>`
1082-
</ul>
1072+
<ol>
1073+
<li>
1074+
<p>If <var>name</var> is a <a>byte-case-insensitive</a> match for one of:
1075+
1076+
<ul class=brief>
1077+
<li>`<code>Accept-Charset</code>`
1078+
<li>`<code>Accept-Encoding</code>`
1079+
<li>`<a http-header><code>Access-Control-Request-Headers</code></a>`
1080+
<li>`<a http-header><code>Access-Control-Request-Method</code></a>`
1081+
<li>`<code>Connection</code>`
1082+
<li>`<code>Content-Length</code>`
1083+
<li>`<code>Cookie</code>`
1084+
<li>`<code>Cookie2</code>`
1085+
<li>`<code>Date</code>`
1086+
<li>`<code>DNT</code>`
1087+
<li>`<code>Expect</code>`
1088+
<li>`<code>Host</code>`
1089+
<li>`<code>Keep-Alive</code>`
1090+
<li>`<a http-header><code>Origin</code></a>`
1091+
<li>`<code>Referer</code>`
1092+
<li>`<code>Set-Cookie</code>`
1093+
<li>`<code>TE</code>`
1094+
<li>`<code>Trailer</code>`
1095+
<li>`<code>Transfer-Encoding</code>`
1096+
<li>`<code>Upgrade</code>`
1097+
<li>`<code>Via</code>`
1098+
</ul>
10831099

1084-
<p>or a <a for=/>header name</a> that when <a>byte-lowercased</a>
1085-
<a for="byte sequence">starts with</a> `<code>proxy-</code>` or `<code>sec-</code>`.
1100+
<p>then return true.
1101+
1102+
<li><p>If <var>name</var> when <a>byte-lowercased</a> <a for="byte sequence">starts with</a>
1103+
`<code>proxy-</code>` or `<code>sec-</code>`, then return true.
1104+
1105+
<li>
1106+
<p>If <var>name</var> is a <a>byte-case-insensitive</a> match for one of:
1107+
1108+
<ul class=brief>
1109+
<li>`<code>X-HTTP-Method</code>`
1110+
<li>`<code>X-HTTP-Method-Override</code>`
1111+
<li>`<code>X-Method-Override</code>`
1112+
</ul>
1113+
1114+
<p>then:
1115+
1116+
<ol>
1117+
<li><p>Let <var>parsedValues</var> be the result of
1118+
<a for="header value">getting, decoding, and splitting</a> <var>value</var>.
1119+
1120+
<li><p><a for=list>For each</a> <var>method</var> in <var>parsedValues</var>: if the
1121+
<a>isomorphic encoding</a> of <var>method</var> is a <a>forbidden method</a>, then return true.
1122+
</ol>
1123+
1124+
<li><p>Return false.
1125+
</ol>
10861126

10871127
<div class=note>
10881128
<p>These are forbidden so the user agent remains in full control over them.
@@ -1438,7 +1478,7 @@ is unset.
14381478
<p class="note no-backref">The <a>unsafe-request flag</a> is set by APIs such as
14391479
<a method><code>fetch()</code></a> and {{XMLHttpRequest}} to ensure a <a>CORS-preflight fetch</a>
14401480
is done based on the supplied <a for=request>method</a> and <a for=request>header list</a>. It does
1441-
not free an API from outlawing <a>forbidden methods</a> and <a>forbidden header names</a>.
1481+
not free an API from outlawing <a>forbidden methods</a> and <a>forbidden request-headers</a>.
14421482

14431483
<p>A <a for=/>request</a> has an associated
14441484
<dfn export for=request id=concept-request-body>body</dfn> (null, a <a for=/>byte sequence</a>, or a
@@ -6369,7 +6409,7 @@ new Headers(meta2);
63696409
<a>throw</a> a {{TypeError}}.
63706410

63716411
<li><p>Otherwise, if <var>headers</var>'s <a for=Headers>guard</a> is "<code>request</code>" and
6372-
<var>name</var> is a <a>forbidden header name</a>, return.
6412+
(<var>name</var>, <var>value</var>) is a <a>forbidden request-header</a>, return.
63736413

63746414
<li>
63756415
<p>Otherwise, if <var>headers</var>'s <a for=Headers>guard</a> is "<code>request-no-cors</code>":
@@ -6395,7 +6435,7 @@ new Headers(meta2);
63956435
<a for=Headers>header list</a>.
63966436

63976437
<li><p>If <var>headers</var>'s <a for=Headers>guard</a> is "<code>request-no-cors</code>", then
6398-
<a for=Headers>remove privileged no-CORS request headers</a> from <var>headers</var>.
6438+
<a for=Headers>remove privileged no-CORS request-headers</a> from <var>headers</var>.
63996439
</ol>
64006440

64016441
<p>To <dfn export for=Headers id=concept-headers-fill>fill</dfn> a {{Headers}} object
@@ -6420,7 +6460,7 @@ new Headers(meta2);
64206460
</ol>
64216461

64226462
<p>To
6423-
<dfn for=Headers id=concept-headers-remove-privileged-no-cors-request-headers>remove privileged no-CORS request headers</dfn>
6463+
<dfn for=Headers id=concept-headers-remove-privileged-no-cors-request-headers>remove privileged no-CORS request-headers</dfn>
64246464
from a {{Headers}} object (<var>headers</var>), run these steps:
64256465

64266466
<ol>
@@ -6456,8 +6496,12 @@ method steps are to <a for=Headers>append</a> (<var>name</var>, <var>value</var>
64566496
<li><p>If <a>this</a>'s <a for=Headers>guard</a> is "<code>immutable</code>", then <a>throw</a> a
64576497
{{TypeError}}.
64586498

6459-
<li><p>Otherwise, if <a>this</a>'s <a for=Headers>guard</a> is "<code>request</code>" and
6460-
<var>name</var> is a <a>forbidden header name</a>, return.
6499+
<li>
6500+
<p>Otherwise, if <a>this</a>'s <a for=Headers>guard</a> is "<code>request</code>" and
6501+
(<var>name</var>, ``) is a <a>forbidden request-header</a>, return.
6502+
6503+
<p class=note>Passing a dummy <a>header value</a> to <a>forbidden request-header</a> ought not to
6504+
have any negative repercussions.
64616505

64626506
<li><p>Otherwise, if <a>this</a>'s <a for=Headers>guard</a> is "<code>request-no-cors</code>",
64636507
<var>name</var> is not a <a>no-CORS-safelisted request-header name</a>, and <var>name</var> is not
@@ -6473,7 +6517,7 @@ method steps are to <a for=Headers>append</a> (<var>name</var>, <var>value</var>
64736517
<a for=Headers>header list</a>.
64746518

64756519
<li><p>If <a>this</a>'s <a for=Headers>guard</a> is "<code>request-no-cors</code>", then
6476-
<a for=Headers>remove privileged no-CORS request headers</a> from <a>this</a>.
6520+
<a for=Headers>remove privileged no-CORS request-headers</a> from <a>this</a>.
64776521
</ol>
64786522

64796523
<p>The <dfn export for=Headers method><code>get(<var>name</var>)</code></dfn> method steps are:
@@ -6507,7 +6551,7 @@ method steps are:
65076551
{{TypeError}}.
65086552

65096553
<li><p>Otherwise, if <a>this</a>'s <a for=Headers>guard</a> is "<code>request</code>" and
6510-
<var>name</var> is a <a>forbidden header name</a>, return.
6554+
(<var>name</var>, <var>value</var>) is a <a>forbidden request-header</a>, return.
65116555

65126556
<li><p>Otherwise, if <a>this</a>'s <a for=Headers>guard</a> is "<code>request-no-cors</code>" and
65136557
(<var>name</var>, <var>value</var>) is not a <a>no-CORS-safelisted request-header</a>, return.
@@ -6519,7 +6563,7 @@ method steps are:
65196563
<a for=Headers>header list</a>.
65206564

65216565
<li><p>If <a>this</a>'s <a for=Headers>guard</a> is "<code>request-no-cors</code>", then
6522-
<a for=Headers>remove privileged no-CORS request headers</a> from <a>this</a>.
6566+
<a for=Headers>remove privileged no-CORS request-headers</a> from <a>this</a>.
65236567
</ol>
65246568

65256569
<p>The <a>value pairs to iterate over</a> are the return value of running
@@ -8089,20 +8133,15 @@ that RFC's normative processing requirements to be compatible with deployed cont
80898133

80908134
<h3 id=http-header-layer-division dfn class=no-num>HTTP header layer division</h3>
80918135

8092-
<p>For the purposes of fetching, there is an API layer (HTML's
8093-
<code>img</code>, CSS' <code>background-image</code>), early fetch layer,
8094-
service worker layer, and network &amp; cache layer.
8095-
`<code>Accept</code>` and
8096-
`<code>Accept-Language</code>` are set in the early fetch layer
8136+
<p>For the purposes of fetching, there is an API layer (HTML's <code>img</code>, CSS's
8137+
<code>background-image</code>), early fetch layer, service worker layer, and network &amp; cache
8138+
layer. `<code>Accept</code>` and `<code>Accept-Language</code>` are set in the early fetch layer
80978139
(typically by the user agent). Most other headers controlled by the user agent, such as
8098-
`<code>Accept-Encoding</code>`,
8099-
`<code>Host</code>`, and `<code>Referer</code>`, are
8100-
set in the network &amp; cache layer. Developers can set headers either at the API layer
8101-
or in the service worker layer (typically through a {{Request}} object).
8102-
Developers have almost no control over
8103-
<a lt="forbidden header name">forbidden headers</a>, but can control
8104-
`<code>Accept</code>` and have the means to constrain and omit
8105-
`<code>Referer</code>` for instance.
8140+
`<code>Accept-Encoding</code>`, `<code>Host</code>`, and `<code>Referer</code>`, are set in the
8141+
network &amp; cache layer. Developers can set headers either at the API layer or in the service
8142+
worker layer (typically through a {{Request}} object). Developers have almost no control over
8143+
<a>forbidden request-headers</a>, but can control `<code>Accept</code>` and have the means to
8144+
constrain and omit `<code>Referer</code>` for instance.
81068145

81078146

81088147
<h3 id=atomic-http-redirect-handling dfn class=no-num>Atomic HTTP redirect handling</h3>
@@ -8404,6 +8443,7 @@ Marijn Kruisselbrink,
84048443
Mark Nottingham,
84058444
Mark S. Miller,
84068445
Martin Dürst,
8446+
Martin O'Neal<!-- dramatic-bishop; GitHub -->,
84078447
Martin Thomson,
84088448
Matt Andrews,
84098449
Matt Falkenhagen,

0 commit comments

Comments
 (0)