Skip to content

Commit 1fde755

Browse files
committed
Specify structured-header integer to WebIDL conversions
1 parent 231e32b commit 1fde755

File tree

3 files changed

+114
-14
lines changed

3 files changed

+114
-14
lines changed

api.bs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,17 +1922,17 @@ the {{AttributionImpressionOptions}} dictionary passed to
19221922
<dt><dfn noexport><code>histogram-index</code></dfn></dt>
19231923
<dd>
19241924
Value of <a dict-member for=AttributionImpressionOptions>histogramIndex</a>,
1925-
a non-negative [=structured header/integer=]. This key is required.
1925+
an [=structured header/integer=] in the [=32-bit unsigned integer=] range. This key is required.
19261926
</dd>
19271927
<dt><dfn noexport><code>priority</code></dfn></dt>
19281928
<dd>
19291929
Value of <a dict-member for=AttributionImpressionOptions>priority</a>,
1930-
an [=structured header/integer=]. This key is optional.
1930+
an [=structured header/integer=] in the [=32-bit signed integer=] range. This key is optional.
19311931
</dd>
19321932
<dt><dfn noexport><code>match-value</code></dfn></dt>
19331933
<dd>
19341934
Value of <a dict-member for=AttributionImpressionOptions>matchValue</a>,
1935-
a non-negative [=structured header/integer=]. This key is optional.
1935+
an [=structured header/integer=] in the [=32-bit unsigned integer=] range. This key is optional.
19361936
If not supplied, a value of 0 is saved for [=impression/Match Value=].
19371937
</dd>
19381938
<dt><dfn noexport><code>lifetime-days</code></dfn></dt>
@@ -1952,7 +1952,7 @@ To <dfn noexport>parse a `Save-Impression` header</dfn> given a [=header value=]
19521952
<var ignore>field_type</var> set to "`dictionary`".
19531953
1. If parsing failed, return an error.
19541954
1. If |dict|["<code>[=save-impression/histogram-index=]</code>"] does not [=map/exist=] or
1955-
is not an [=structured header/integer=] or is less than 0, return an error.
1955+
is not an [=structured header/integer=] in the [=32-bit unsigned integer=] range, return an error.
19561956
1. Let |histogramIndex| be |dict|["<code>[=save-impression/histogram-index=]</code>"].
19571957
1. Let |conversionSites| be |dict|["<code>[=save-impression/conversion-sites=]</code>"]
19581958
[=map/with default=] an empty [=structured header/inner list=].
@@ -1961,11 +1961,12 @@ To <dfn noexport>parse a `Save-Impression` header</dfn> given a [=header value=]
19611961
[=map/with default=] an empty [=structured header/inner list=].
19621962
1. If |conversionCallers| is not an [=structured header/inner list=], or if any of |conversionCallers|' [=list/items=] is not a [=structured header/string=], return an error.
19631963
1. Let |matchValue| be |dict|["<code>[=save-impression/match-value=]</code>"] [=map/with default=] 0.
1964-
1. If |matchValue| is not an [=structured header/integer=] or is less than 0, return an error.
1964+
1. If |matchValue| is not an [=structured header/integer=] in the [=32-bit unsigned integer=] range, return an error.
19651965
1. Let |lifetimeDays| be |dict|["<code>[=save-impression/lifetime-days=]</code>"] [=map/with default=] 30.
1966-
1. If |lifetimeDays| is not an [=structured header/integer=] or is less than or equal to 0, return an error.
1966+
1. If |lifetimeDays| is not a positive [=structured header/integer=], return an error.
1967+
1. Clamp |lifetimeDays| to the [=32-bit unsigned integer=] range.
19671968
1. Let |priority| be |dict|["<code>[=save-impression/priority=]</code>"] [=map/with default=] 0.
1968-
1. If |priority| is not an [=structured header/integer=], return an error.
1969+
1. If |priority| is not an [=structured header/integer=] in the [=32-bit signed integer=] range, return an error.
19691970
1. Return a new {{AttributionImpressionOptions}} with the following items:
19701971
: {{AttributionImpressionOptions/histogramIndex}}
19711972
:: |histogramIndex|

impl/src/http.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ runTests([
7373
{ name: "histogram-index-negative", input: "histogram-index=-1" },
7474
{ name: "histogram-index-not-integer", input: "histogram-index=1.2" },
7575

76+
{
77+
name: "valid-histogram-index-eq-32-bit-max",
78+
input: "histogram-index=4294967295",
79+
expected: {
80+
histogramIndex: 4294967295,
81+
matchValue: 0,
82+
conversionSites: [],
83+
conversionCallers: [],
84+
lifetimeDays: 30,
85+
priority: 0,
86+
},
87+
},
88+
{
89+
name: "histogram-index-gt-32-bit-max",
90+
input: "histogram-index=4294967296",
91+
},
92+
7693
{
7794
name: "conversion-sites-wrong-type",
7895
input: "conversion-sites=a, histogram-index=1",
@@ -97,6 +114,22 @@ runTests([
97114
name: "match-value-not-integer",
98115
input: "match-value=1.2, histogram-index=1",
99116
},
117+
{
118+
name: "valid-match-value-eq-32-bit-max",
119+
input: "match-value=4294967295, histogram-index=1",
120+
expected: {
121+
histogramIndex: 1,
122+
matchValue: 4294967295,
123+
conversionSites: [],
124+
conversionCallers: [],
125+
lifetimeDays: 30,
126+
priority: 0,
127+
},
128+
},
129+
{
130+
name: "match-value-gt-32-bit-max",
131+
input: "match-value=4294967296, histogram-index=1",
132+
},
100133

101134
{
102135
name: "lifetime-days-wrong-type",
@@ -111,7 +144,51 @@ runTests([
111144
input: "lifetime-days=1.2, histogram-index=1",
112145
},
113146
{ name: "lifetime-days-zero", input: "lifetime-days=0, histogram-index=1" },
147+
{
148+
name: "valid-lifetime-days-maximal-clamped",
149+
input: "lifetime-days=999999999999999, histogram-index=1",
150+
expected: {
151+
histogramIndex: 1,
152+
matchValue: 0,
153+
conversionSites: [],
154+
conversionCallers: [],
155+
lifetimeDays: 4294967295,
156+
priority: 0,
157+
},
158+
},
114159

115160
{ name: "priority-wrong-type", input: "priority=a, histogram-index=1" },
116161
{ name: "priority-not-integer", input: "priority=1.2, histogram-index=1" },
162+
{
163+
name: "valid-priority-eq-32-bit-max",
164+
input: "priority=2147483647, histogram-index=1",
165+
expected: {
166+
histogramIndex: 1,
167+
matchValue: 0,
168+
conversionSites: [],
169+
conversionCallers: [],
170+
lifetimeDays: 30,
171+
priority: 2147483647,
172+
},
173+
},
174+
{
175+
name: "valid-priority-eq-32-bit-min",
176+
input: "priority=-2147483648, histogram-index=1",
177+
expected: {
178+
histogramIndex: 1,
179+
matchValue: 0,
180+
conversionSites: [],
181+
conversionCallers: [],
182+
lifetimeDays: 30,
183+
priority: -2147483648,
184+
},
185+
},
186+
{
187+
name: "priority-gt-32-bit-max",
188+
input: "priority=2147483648, histogram-index=1",
189+
},
190+
{
191+
name: "priority-lt-32-bit-min",
192+
input: "priority=-2147483649, histogram-index=1",
193+
},
117194
]);

impl/src/http.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import type { Dictionary } from "structured-headers";
44

55
import { parseDictionary } from "structured-headers";
66

7+
const MAX_UINT32: number = 4294967295;
8+
9+
const MIN_INT32: number = -2147483648;
10+
const MAX_INT32: number = 2147483647;
11+
712
function parseInnerListOfSites(dict: Dictionary, key: string): string[] {
813
const [values] = dict.get(key) ?? [[]];
914
if (!Array.isArray(values)) {
@@ -29,9 +34,12 @@ export function parseSaveImpressionHeader(
2934
if (
3035
typeof histogramIndex !== "number" ||
3136
!Number.isInteger(histogramIndex) ||
32-
histogramIndex < 0
37+
histogramIndex < 0 ||
38+
histogramIndex > MAX_UINT32
3339
) {
34-
throw new RangeError("histogram-index must be a non-negative integer");
40+
throw new RangeError(
41+
"histogram-index must be an integer in the 32-bit unsigned range",
42+
);
3543
}
3644

3745
const conversionSites = parseInnerListOfSites(dict, "conversion-sites");
@@ -41,12 +49,15 @@ export function parseSaveImpressionHeader(
4149
if (
4250
typeof matchValue !== "number" ||
4351
!Number.isInteger(matchValue) ||
44-
matchValue < 0
52+
matchValue < 0 ||
53+
matchValue > MAX_UINT32
4554
) {
46-
throw new RangeError("match-value must be a non-negative integer");
55+
throw new RangeError(
56+
"match-value must be an integer in the 32-bit unsigned range",
57+
);
4758
}
4859

49-
const [lifetimeDays] = dict.get("lifetime-days") ?? [30];
60+
let [lifetimeDays] = dict.get("lifetime-days") ?? [30];
5061
if (
5162
typeof lifetimeDays !== "number" ||
5263
!Number.isInteger(lifetimeDays) ||
@@ -55,9 +66,20 @@ export function parseSaveImpressionHeader(
5566
throw new RangeError("lifetime-days must be a positive integer");
5667
}
5768

69+
if (lifetimeDays > MAX_UINT32) {
70+
lifetimeDays = MAX_UINT32;
71+
}
72+
5873
const [priority] = dict.get("priority") ?? [0];
59-
if (typeof priority !== "number" || !Number.isInteger(priority)) {
60-
throw new RangeError("priority must be an integer");
74+
if (
75+
typeof priority !== "number" ||
76+
!Number.isInteger(priority) ||
77+
priority < MIN_INT32 ||
78+
priority > MAX_INT32
79+
) {
80+
throw new RangeError(
81+
"priority must be an integer in the 32-bit signed range",
82+
);
6183
}
6284

6385
return {

0 commit comments

Comments
 (0)