From 2e58759c35d172eccc7c91f7294970d51b9eb75d Mon Sep 17 00:00:00 2001 From: Kershaw Chang Date: Tue, 17 Nov 2020 11:07:03 +0000 Subject: [PATCH] Bug 1677086 - Parse SvcParamKeyAlpn as defined in spec r=necko-reviewers,valentin Differential Revision: https://phabricator.services.mozilla.com/D97000 --- netwerk/base/Dashboard.cpp | 12 +++++-- netwerk/dns/HTTPSSVC.cpp | 14 ++++---- netwerk/dns/HTTPSSVC.h | 2 +- netwerk/dns/TRR.cpp | 16 +++++++-- netwerk/dns/nsIDNSByTypeRecord.idl | 2 +- netwerk/protocol/http/nsHttp.cpp | 10 ++---- netwerk/protocol/http/nsHttp.h | 4 +-- netwerk/test/gtest/TestSupportAlpn.cpp | 22 ++++++++----- netwerk/test/unit/test_httpssvc_iphint.js | 6 ++-- netwerk/test/unit/test_httpssvc_priority.js | 2 +- netwerk/test/unit/test_trr_https_fallback.js | 34 ++++++++++---------- netwerk/test/unit/test_trr_httpssvc.js | 24 +++++++------- testing/xpcshell/dns-packet/index.js | 33 +++++++++++++++---- testing/xpcshell/moz-http2/moz-http2.js | 2 +- 14 files changed, 112 insertions(+), 71 deletions(-) diff --git a/netwerk/base/Dashboard.cpp b/netwerk/base/Dashboard.cpp index 13ce9445c2b1..a771f46264b8 100644 --- a/netwerk/base/Dashboard.cpp +++ b/netwerk/base/Dashboard.cpp @@ -376,9 +376,15 @@ nsresult LookupHelper::ConstructHTTPSRRAnswer(LookupArgument* aArgument) { nextRecord->mAlpn.Construct(); nextRecord->mAlpn.Value().mType = type; nsCOMPtr alpnParam = do_QueryInterface(value); - nsCString alpnStr; - Unused << alpnParam->GetAlpn(alpnStr); - CopyASCIItoUTF16(alpnStr, nextRecord->mAlpn.Value().mAlpn); + nsTArray alpn; + Unused << alpnParam->GetAlpn(alpn); + nsAutoCString alpnStr; + for (const auto& str : alpn) { + alpnStr.Append(str); + alpnStr.Append(','); + } + CopyASCIItoUTF16(Span(alpnStr.BeginReading(), alpnStr.Length() - 1), + nextRecord->mAlpn.Value().mAlpn); break; } case SvcParamKeyNoDefaultAlpn: { diff --git a/netwerk/dns/HTTPSSVC.cpp b/netwerk/dns/HTTPSSVC.cpp index 7758217c4dfa..d16a341bd046 100644 --- a/netwerk/dns/HTTPSSVC.cpp +++ b/netwerk/dns/HTTPSSVC.cpp @@ -67,12 +67,12 @@ SvcParam::GetType(uint16_t* aType) { } NS_IMETHODIMP -SvcParam::GetAlpn(nsACString& aAlpn) { +SvcParam::GetAlpn(nsTArray& aAlpn) { if (!mValue.is()) { MOZ_ASSERT(false, "Unexpected type for variant"); return NS_ERROR_NOT_AVAILABLE; } - aAlpn = mValue.as().mValue; + aAlpn.AppendElements(mValue.as().mValue); return NS_OK; } @@ -172,13 +172,13 @@ bool SVCB::NoDefaultAlpn() const { Maybe> SVCB::GetAlpn(bool aNoHttp2, bool aNoHttp3) const { Maybe> alpn; - nsAutoCString alpnValue; for (const auto& value : mSvcFieldValue) { if (value.mValue.is()) { - alpn.emplace(); - alpnValue = value.mValue.as().mValue; - if (!alpnValue.IsEmpty()) { - alpn = Some(SelectAlpnFromAlpnList(alpnValue, aNoHttp2, aNoHttp3)); + nsTArray alpnList; + alpnList.AppendElements(value.mValue.as().mValue); + if (!alpnList.IsEmpty()) { + alpn.emplace(); + alpn = Some(SelectAlpnFromAlpnList(alpnList, aNoHttp2, aNoHttp3)); } return alpn; } diff --git a/netwerk/dns/HTTPSSVC.h b/netwerk/dns/HTTPSSVC.h index de4c1161062f..9bec7aa696e5 100644 --- a/netwerk/dns/HTTPSSVC.h +++ b/netwerk/dns/HTTPSSVC.h @@ -29,7 +29,7 @@ struct SvcParamAlpn { bool operator==(const SvcParamAlpn& aOther) const { return mValue == aOther.mValue; } - nsCString mValue; + CopyableTArray mValue; }; struct SvcParamNoDefaultAlpn { diff --git a/netwerk/dns/TRR.cpp b/netwerk/dns/TRR.cpp index a56f58c320b2..1409dc66e1b7 100644 --- a/netwerk/dns/TRR.cpp +++ b/netwerk/dns/TRR.cpp @@ -1399,8 +1399,20 @@ nsresult TRR::ParseSvcParam(unsigned int svcbIndex, uint16_t key, break; } case SvcParamKeyAlpn: { - field.mValue = AsVariant(SvcParamAlpn{ - .mValue = nsCString((const char*)(&mResponse[svcbIndex]), length)}); + field.mValue = AsVariant(SvcParamAlpn()); + auto& alpnArray = field.mValue.as().mValue; + while (length > 0) { + uint8_t alpnIdLength = mResponse[svcbIndex++]; + length -= 1; + if (alpnIdLength > length) { + return NS_ERROR_UNEXPECTED; + } + + alpnArray.AppendElement( + nsCString((const char*)&mResponse[svcbIndex], alpnIdLength)); + length -= alpnIdLength; + svcbIndex += alpnIdLength; + } break; } case SvcParamKeyNoDefaultAlpn: { diff --git a/netwerk/dns/nsIDNSByTypeRecord.idl b/netwerk/dns/nsIDNSByTypeRecord.idl index 88a308274f71..b6cc35397d1b 100644 --- a/netwerk/dns/nsIDNSByTypeRecord.idl +++ b/netwerk/dns/nsIDNSByTypeRecord.idl @@ -60,7 +60,7 @@ interface nsISVCParam : nsISupports { [scriptable, uuid(0dc58309-4d67-4fc4-a4e3-38dbde9d9f4c)] interface nsISVCParamAlpn : nsISupports { - readonly attribute ACString alpn; + readonly attribute Array alpn; }; [scriptable, uuid(b3ed89c3-2ae6-4c92-8176-b76bc2437fcb)] diff --git a/netwerk/protocol/http/nsHttp.cpp b/netwerk/protocol/http/nsHttp.cpp index 1a7ecc04ca67..d6f8443c01c6 100644 --- a/netwerk/protocol/http/nsHttp.cpp +++ b/netwerk/protocol/http/nsHttp.cpp @@ -1003,16 +1003,12 @@ nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode) { return rv; } -Tuple SelectAlpnFromAlpnList(const nsACString& aAlpnList, - bool aNoHttp2, bool aNoHttp3) { +Tuple SelectAlpnFromAlpnList( + const nsTArray& aAlpnList, bool aNoHttp2, bool aNoHttp3) { nsCString h3Value; nsCString h2Value; nsCString h1Value; - // aAlpnList is a list of alpn-id and use comma as a delimiter. - nsCCharSeparatedTokenizer tokenizer(aAlpnList, ','); - nsAutoCString npnStr; - while (tokenizer.hasMoreTokens()) { - const nsACString& npnToken(tokenizer.nextToken()); + for (const auto& npnToken : aAlpnList) { bool isHttp3 = gHttpHandler->IsHttp3VersionSupported(npnToken); if (isHttp3 && h3Value.IsEmpty()) { h3Value.Assign(npnToken); diff --git a/netwerk/protocol/http/nsHttp.h b/netwerk/protocol/http/nsHttp.h index be98da9e5197..be9486fff415 100644 --- a/netwerk/protocol/http/nsHttp.h +++ b/netwerk/protocol/http/nsHttp.h @@ -379,8 +379,8 @@ nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode); // Tuple. The first element is the alpn-id and the second one // is a boolean to indicate if this alpn-id is for http3. If no supported // alpn-id is found, the first element would be a n empty string. -Tuple SelectAlpnFromAlpnList(const nsACString& aAlpnList, - bool aNoHttp2, bool aNoHttp3); +Tuple SelectAlpnFromAlpnList( + const nsTArray& aAlpnList, bool aNoHttp2, bool aNoHttp3); } // namespace net } // namespace mozilla diff --git a/netwerk/test/gtest/TestSupportAlpn.cpp b/netwerk/test/gtest/TestSupportAlpn.cpp index d18c4d0ee8d3..ee67c854e61f 100644 --- a/netwerk/test/gtest/TestSupportAlpn.cpp +++ b/netwerk/test/gtest/TestSupportAlpn.cpp @@ -15,38 +15,44 @@ TEST(TestSupportAlpn, testSvcParamKeyAlpn) do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http"); // h2 and h3 are enabled, so first appearance h3 alpn-id is returned. - Tuple result = - SelectAlpnFromAlpnList("h3-28,h3-27,h2,http/1.1"_ns, false, false); + nsTArray alpn = {"h3-28"_ns, "h3-27"_ns, "h2"_ns, "http/1.1"_ns}; + Tuple result = SelectAlpnFromAlpnList(alpn, false, false); ASSERT_EQ(Get<0>(result), "h3-28"_ns); ASSERT_EQ(Get<1>(result), true); // We don't support h3-26, so we should choose h2. - result = SelectAlpnFromAlpnList("h3-26,h2,http/1.1"_ns, false, false); + alpn = {"h3-26"_ns, "h2"_ns, "http/1.1"_ns}; + result = SelectAlpnFromAlpnList(alpn, false, false); ASSERT_EQ(Get<0>(result), "h2"_ns); ASSERT_EQ(Get<1>(result), false); // h3 is disabled, so we will use h2. - result = SelectAlpnFromAlpnList("h3-28,h3-27,h2,http/1.1"_ns, false, true); + alpn = {"h3-28"_ns, "h3-27"_ns, "h2"_ns, "http/1.1"_ns}; + result = SelectAlpnFromAlpnList(alpn, false, true); ASSERT_EQ(Get<0>(result), "h2"_ns); ASSERT_EQ(Get<1>(result), false); // h3 is disabled and h2 is not found, so we will select http/1.1. - result = SelectAlpnFromAlpnList("h3-28,h3-27,http/1.1"_ns, false, true); + alpn = {"h3-28"_ns, "h3-27"_ns, "http/1.1"_ns}; + result = SelectAlpnFromAlpnList(alpn, false, true); ASSERT_EQ(Get<0>(result), "http/1.1"_ns); ASSERT_EQ(Get<1>(result), false); // h3 and h2 are disabled, so we will select http/1.1. - result = SelectAlpnFromAlpnList("h3-28,h3-27,h2,http/1.1"_ns, true, true); + alpn = {"h3-28"_ns, "h3-27"_ns, "h2"_ns, "http/1.1"_ns}; + result = SelectAlpnFromAlpnList(alpn, true, true); ASSERT_EQ(Get<0>(result), "http/1.1"_ns); ASSERT_EQ(Get<1>(result), false); // h3 and h2 are disabled and http1.1 is not found, we return an empty string. - result = SelectAlpnFromAlpnList("h3-28,h3-27,h2"_ns, true, true); + alpn = {"h3-28"_ns, "h3-27"_ns, "h2"_ns}; + result = SelectAlpnFromAlpnList(alpn, true, true); ASSERT_EQ(Get<0>(result), ""_ns); ASSERT_EQ(Get<1>(result), false); // No supported alpn. - result = SelectAlpnFromAlpnList("ftp,h2c"_ns, true, true); + alpn = {"ftp"_ns, "h2c"_ns}; + result = SelectAlpnFromAlpnList(alpn, true, true); ASSERT_EQ(Get<0>(result), ""_ns); ASSERT_EQ(Get<1>(result), false); } diff --git a/netwerk/test/unit/test_httpssvc_iphint.js b/netwerk/test/unit/test_httpssvc_iphint.js index 5845d63ed786..1b37e6a51e92 100644 --- a/netwerk/test/unit/test_httpssvc_iphint.js +++ b/netwerk/test/unit/test_httpssvc_iphint.js @@ -127,7 +127,7 @@ add_task(async function testStoreIPHint() { priority: 1, name: "test.IPHint.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "port", value: 8888 }, { key: "ipv4hint", value: ["1.2.3.4", "5.6.7.8"] }, { key: "ipv6hint", value: ["::1", "fe80::794f:6d2c:3d5e:7836"] }, @@ -156,9 +156,9 @@ add_task(async function testStoreIPHint() { Assert.equal(answer[0].priority, 1); Assert.equal(answer[0].name, "test.IPHint.com"); Assert.equal(answer[0].values.length, 4); - Assert.equal( + Assert.deepEqual( answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn, - "h2,h3", + ["h2", "h3"], "got correct answer" ); Assert.equal( diff --git a/netwerk/test/unit/test_httpssvc_priority.js b/netwerk/test/unit/test_httpssvc_priority.js index 3b0c73f8be3b..40bd26810f88 100644 --- a/netwerk/test/unit/test_httpssvc_priority.js +++ b/netwerk/test/unit/test_httpssvc_priority.js @@ -121,7 +121,7 @@ add_task(async function testPriorityAndECHConfig() { data: { priority: 1, name: "test.p1.com", - values: [{ key: "alpn", value: "h2,h3" }], + values: [{ key: "alpn", value: ["h2", "h3"] }], }, }, { diff --git a/netwerk/test/unit/test_trr_https_fallback.js b/netwerk/test/unit/test_trr_https_fallback.js index 2df3f3c01a11..427b259ae992 100644 --- a/netwerk/test/unit/test_trr_https_fallback.js +++ b/netwerk/test/unit/test_trr_https_fallback.js @@ -159,7 +159,7 @@ add_task(async function testFallbackToTheLastRecord() { priority: 1, name: "test.fallback1.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "123..." }, ], }, @@ -173,7 +173,7 @@ add_task(async function testFallbackToTheLastRecord() { priority: 4, name: "foo.example.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "port", value: h2Port }, { key: "echconfig", value: "456..." }, ], @@ -188,7 +188,7 @@ add_task(async function testFallbackToTheLastRecord() { priority: 3, name: "test.fallback3.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -202,7 +202,7 @@ add_task(async function testFallbackToTheLastRecord() { priority: 2, name: "test.fallback2.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -262,7 +262,7 @@ add_task(async function testFallbackToTheOrigin() { priority: 1, name: "test.foo1.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "123..." }, ], }, @@ -276,7 +276,7 @@ add_task(async function testFallbackToTheOrigin() { priority: 3, name: "test.foo3.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -290,7 +290,7 @@ add_task(async function testFallbackToTheOrigin() { priority: 2, name: "test.foo2.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -361,7 +361,7 @@ add_task(async function testAllRecordsFailed() { priority: 1, name: "test.bar1.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "123..." }, ], }, @@ -375,7 +375,7 @@ add_task(async function testAllRecordsFailed() { priority: 3, name: "test.bar3.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -389,7 +389,7 @@ add_task(async function testAllRecordsFailed() { priority: 2, name: "test.bar2.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -447,7 +447,7 @@ add_task(async function testFallbackToTheOrigin2() { data: { priority: 1, name: "test.example1.com", - values: [{ key: "alpn", value: "h2,h3" }], + values: [{ key: "alpn", value: ["h2", "h3"] }], }, }, { @@ -458,7 +458,7 @@ add_task(async function testFallbackToTheOrigin2() { data: { priority: 3, name: "test.example3.com", - values: [{ key: "alpn", value: "h2,h3" }], + values: [{ key: "alpn", value: ["h2", "h3"] }], }, }, ]); @@ -539,7 +539,7 @@ add_task(async function testFallbackToTheOrigin3() { priority: 1, name: "vulnerable1.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -553,7 +553,7 @@ add_task(async function testFallbackToTheOrigin3() { priority: 2, name: "vulnerable2.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, @@ -566,7 +566,7 @@ add_task(async function testFallbackToTheOrigin3() { data: { priority: 3, name: "vulnerable3.com", - values: [{ key: "alpn", value: "h2,h3" }], + values: [{ key: "alpn", value: ["h2", "h3"] }], }, }, ]); @@ -624,7 +624,7 @@ add_task(async function testResetExclusionList() { priority: 1, name: "test.reset1.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "port", value: h2Port }, { key: "echconfig", value: "456..." }, ], @@ -639,7 +639,7 @@ add_task(async function testResetExclusionList() { priority: 2, name: "test.reset2.com", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "echconfig", value: "456..." }, ], }, diff --git a/netwerk/test/unit/test_trr_httpssvc.js b/netwerk/test/unit/test_trr_httpssvc.js index c50979831c8e..4684c10faff4 100644 --- a/netwerk/test/unit/test_trr_httpssvc.js +++ b/netwerk/test/unit/test_trr_httpssvc.js @@ -134,9 +134,9 @@ add_task(async function testHTTPSSVC() { Assert.equal(answer[0].priority, 1); Assert.equal(answer[0].name, "h3pool"); Assert.equal(answer[0].values.length, 6); - Assert.equal( + Assert.deepEqual( answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn, - "h2,h3", + ["h2", "h3"], "got correct answer" ); Assert.ok( @@ -168,9 +168,9 @@ add_task(async function testHTTPSSVC() { Assert.equal(answer[1].priority, 2); Assert.equal(answer[1].name, "test.httpssvc.com"); Assert.equal(answer[1].values.length, 4); - Assert.equal( + Assert.deepEqual( answer[1].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn, - "h2", + ["h2"], "got correct answer" ); Assert.equal( @@ -310,7 +310,7 @@ add_task(async function test_aliasform() { priority: 1, name: "h3pool", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "no-default-alpn" }, { key: "port", value: 8888 }, { key: "ipv4hint", value: "1.2.3.4" }, @@ -396,7 +396,7 @@ add_task(async function test_aliasform() { priority: 1, name: "h3pool", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "no-default-alpn" }, { key: "port", value: 8888 }, { key: "ipv4hint", value: "1.2.3.4" }, @@ -452,7 +452,7 @@ add_task(async function test_aliasform() { { key: "ipv4hint", value: "1.2.3.4" }, { key: "port", value: 8888 }, { key: "no-default-alpn" }, - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, ], }, }, @@ -487,8 +487,8 @@ add_task(async function test_aliasform() { priority: 1, name: "h3pool", values: [ - { key: "alpn", value: "h2,h3" }, - { key: "alpn", value: "h2,h3,h4" }, + { key: "alpn", value: ["h2", "h3"] }, + { key: "alpn", value: ["h2", "h3", "h4"] }, ], }, }, @@ -524,7 +524,7 @@ add_task(async function test_aliasform() { name: "h3pool", values: [ { key: "mandatory", value: ["key100"] }, - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "key100" }, ], }, @@ -568,7 +568,7 @@ add_task(async function test_aliasform() { "ipv6hint", ], }, - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "no-default-alpn" }, { key: "port", value: 8888 }, { key: "ipv4hint", value: "1.2.3.4" }, @@ -634,7 +634,7 @@ add_task(async function test_aliasform() { data: { priority: 1, name: ".", - values: [{ key: "alpn", value: "h2,h3" }], + values: [{ key: "alpn", value: ["h2", "h3"] }], }, }, ]); diff --git a/testing/xpcshell/dns-packet/index.js b/testing/xpcshell/dns-packet/index.js index 263b3f08f6a1..de82b88a3f40 100644 --- a/testing/xpcshell/dns-packet/index.js +++ b/testing/xpcshell/dns-packet/index.js @@ -1347,13 +1347,27 @@ svcparam.encode = function(param, buf, offset) { svcparam.encode.bytes += 2; } } else if (key == 1) { // alpn - let len = param.value.length - buf.writeUInt16BE(len || 0, offset); + let val = param.value; + if (!Array.isArray(val)) val = [val]; + // The alpn param is prefixed by its length as a single byte, so the + // initialValue to reduce function is the length of the array. + let total = val.reduce(function(result, id) { + return result += id.length; + }, val.length); + + buf.writeUInt16BE(total, offset); offset += 2; svcparam.encode.bytes += 2; - buf.write(param.value, offset) - offset += len; - svcparam.encode.bytes += len; + + for (let id of val) { + buf.writeUInt8(id.length, offset); + offset += 1; + svcparam.encode.bytes += 1; + + buf.write(id, offset); + offset += id.length; + svcparam.encode.bytes += id.length; + } } else if (key == 2) { // no-default-alpn buf.writeUInt16BE(0, offset); offset += 2; @@ -1433,7 +1447,14 @@ svcparam.encodingLength = function (param) { switch (param.key) { case 'mandatory' : return 4 + 2*(Array.isArray(param.value) ? param.value.length : 1) - case 'alpn' : return 4 + param.value.length + case 'alpn' : { + let val = param.value; + if (!Array.isArray(val)) val = [val]; + let total = val.reduce(function(result, id) { + return result += id.length; + }, val.length); + return 4 + total; + } case 'no-default-alpn' : return 4 case 'port' : return 4 + 2 case 'ipv4hint' : return 4 + 4 * (Array.isArray(param.value) ? param.value.length : 1) diff --git a/testing/xpcshell/moz-http2/moz-http2.js b/testing/xpcshell/moz-http2/moz-http2.js index 52f0d95055ef..503f3cc99616 100644 --- a/testing/xpcshell/moz-http2/moz-http2.js +++ b/testing/xpcshell/moz-http2/moz-http2.js @@ -849,7 +849,7 @@ function handleRequest(req, res) { priority: 1, name: "h3pool", values: [ - { key: "alpn", value: "h2,h3" }, + { key: "alpn", value: ["h2", "h3"] }, { key: "no-default-alpn" }, { key: "port", value: 8888 }, { key: "ipv4hint", value: "1.2.3.4" },