/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let trrServer;

const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

add_setup(async function setup() {
  trr_test_setup();
  registerCleanupFunction(async () => {
    trr_clear_prefs();
    Services.prefs.clearUserPref("network.http.http3.support_version1");
    Services.prefs.clearUserPref("security.tls.version.max");
    if (trrServer) {
      await trrServer.stop();
    }
  });

  if (mozinfo.socketprocess_networking) {
    Services.dns; // Needed to trigger socket process.
    await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
  }
});

function checkResult(inRecord, noHttp2, noHttp3, result) {
  if (!result) {
    Assert.throws(
      () => {
        inRecord
          .QueryInterface(Ci.nsIDNSHTTPSSVCRecord)
          .GetServiceModeRecord(noHttp2, noHttp3);
      },
      /NS_ERROR_NOT_AVAILABLE/,
      "Should get an error"
    );
    return;
  }

  let record = inRecord
    .QueryInterface(Ci.nsIDNSHTTPSSVCRecord)
    .GetServiceModeRecord(noHttp2, noHttp3);
  Assert.equal(record.priority, result.expectedPriority);
  Assert.equal(record.name, result.expectedName);
  Assert.equal(record.selectedAlpn, result.expectedAlpn);
}

add_task(async function testSortedAlpnH3() {
  Services.dns.clearCache(true);

  trrServer = new TRRServer();
  await trrServer.start();

  Services.prefs.setIntPref("network.trr.mode", 3);
  Services.prefs.setCharPref(
    "network.trr.uri",
    `https://foo.example.com:${trrServer.port()}/dns-query`
  );
  Services.prefs.setBoolPref("network.http.http3.support_version1", true);
  await trrServer.registerDoHAnswers("test.alpn.com", "HTTPS", {
    answers: [
      {
        name: "test.alpn.com",
        ttl: 55,
        type: "HTTPS",
        flush: false,
        data: {
          priority: 1,
          name: "test.alpn.com",
          values: [{ key: "alpn", value: ["h2", "http/1.1", "h3"] }],
        },
      },
    ],
  });

  let { inRecord } = await new TRRDNSListener("test.alpn.com", {
    type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
  });

  checkResult(inRecord, false, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "h3",
  });
  checkResult(inRecord, false, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, true, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "h3",
  });
  checkResult(inRecord, true, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "http/1.1",
  });

  Services.prefs.setBoolPref("network.http.http3.support_version1", false);
  checkResult(inRecord, false, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, false, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, true, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "http/1.1",
  });
  checkResult(inRecord, true, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "http/1.1",
  });
  Services.prefs.setBoolPref("network.http.http3.support_version1", true);

  // Disable TLS1.3
  Services.prefs.setIntPref("security.tls.version.max", 3);
  checkResult(inRecord, false, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, false, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, true, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "http/1.1",
  });
  checkResult(inRecord, true, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "http/1.1",
  });

  // Enable TLS1.3
  Services.prefs.setIntPref("security.tls.version.max", 4);
  checkResult(inRecord, false, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "h3",
  });
  checkResult(inRecord, false, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, true, false, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "h3",
  });
  checkResult(inRecord, true, true, {
    expectedPriority: 1,
    expectedName: "test.alpn.com",
    expectedAlpn: "http/1.1",
  });
});

add_task(async function testSortedAlpnH2() {
  Services.dns.clearCache(true);

  Services.prefs.setIntPref("network.trr.mode", 3);
  Services.prefs.setCharPref(
    "network.trr.uri",
    `https://foo.example.com:${trrServer.port()}/dns-query`
  );
  await trrServer.registerDoHAnswers("test.alpn_2.com", "HTTPS", {
    answers: [
      {
        name: "test.alpn_2.com",
        ttl: 55,
        type: "HTTPS",
        flush: false,
        data: {
          priority: 1,
          name: "test.alpn_2.com",
          values: [{ key: "alpn", value: ["http/1.1", "h2"] }],
        },
      },
    ],
  });

  let { inRecord } = await new TRRDNSListener("test.alpn_2.com", {
    type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
  });

  checkResult(inRecord, false, false, {
    expectedPriority: 1,
    expectedName: "test.alpn_2.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, false, true, {
    expectedPriority: 1,
    expectedName: "test.alpn_2.com",
    expectedAlpn: "",
  });
  checkResult(inRecord, true, false, {
    expectedPriority: 1,
    expectedName: "test.alpn_2.com",
    expectedAlpn: "http/1.1",
  });
  checkResult(inRecord, true, true, {
    expectedPriority: 1,
    expectedName: "test.alpn_2.com",
    expectedAlpn: "http/1.1",
  });

  await trrServer.stop();
  trrServer = null;
});
