Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  test_he_client_auth_pause.js   Sprache: JAVA

 

/* 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";

// Verifies the Happy Eyeballs client-auth pause: when a TLS server requests
// a client cert from an HE racer, HE stops polling the state machine until
// the prompt is resolved, so no new connection attempts are started while the
// cert-selection dialog is up. Already-running attempts are not cancelled and
// may show their own prompts (allowed), so the dialog can fire more than once.
//
// Setup: Node HTTPS server with requestCert=true. Node listens on the IPv6
// wildcard `::` which on Linux/macOS accepts both IPv4 (via IPv4-mapped
// addresses) and IPv6 connections. The test channel targets `localhost`,
// which on /etc/hosts resolves to BOTH 127.0.0.1 and ::1 — that's what
// gives HE two address families to race. The dialog mock delays its
// response to keep the holding racer parked while the
// connection-attempt-delay timer fires.
//
// Assertion: the request succeeds after a cert is selected and the dialog is
// invoked at least once for the requested hostname. We don't assert an exact
// count: whether the second racer started before the pause is timing
// dependent.

/* import-globals-from head_channels.js */

const { MockRegistrar } = ChromeUtils.importESModule(
  "resource://testing-common/MockRegistrar.sys.mjs"
);
const { NodeHTTPSServer } = ChromeUtils.importESModule(
  "resource://testing-common/NodeServer.sys.mjs"
);
const { setTimeout } = ChromeUtils.importESModule(
  "resource://gre/modules/Timer.sys.mjs"
);
const ctypes = ChromeUtils.importESModule(
  "resource://gre/modules/ctypes.sys.mjs"
).ctypes;

// Minimal inlined copy of head_psm.js's loadPKCS11Module — the full file
// can't be loaded here because it conflicts with netwerk's default
// head_channels.js (both declare XPCOMUtils).
async function loadPKCS11Module(libraryFile, moduleName) {
  Assert.ok(libraryFile.exists(), "PKCS11 module file should exist");
  let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
    Ci.nsIPKCS11ModuleDB
  );
  registerCleanupFunction(async () => {
    try {
      await pkcs11ModuleDB.deleteModule(moduleName);
    } catch (e) {
      /* ignore */
    }
  });
  await pkcs11ModuleDB.addModule(moduleName, libraryFile.path, 0, 0);
}

const TEST_BROWSER_ID = 4242;
// Long enough to comfortably exceed HE's connection-attempt-delay (set
// below) so the second racer would fire well before our dialog responds.
const DIALOG_RESPONSE_DELAY_MS = 1500;
const HE_CONNECTION_ATTEMPT_DELAY_MS = 50;

let gServer;
let gChooseCertificateInvocations = 0;
let gChooseCertificateHostnames = [];

const gClientAuthDialogService = {
  QueryInterface: ChromeUtils.generateQI(["nsIClientAuthDialogService"]),

  chooseCertificate(hostname, certArray, _loadContext, _caNames, callback) {
    gChooseCertificateInvocations++;
    gChooseCertificateHostnames.push(hostname);
    // The delay keeps the holder's TLS handshake parked while HE's
    // connection-attempt-delay timer fires. With the pause working, polling
    // is stopped so no new attempt is started off that timer.
    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    setTimeout(() => {
      if (certArray && certArray.length) {
        callback.certificateChosen(certArray[0], false);
      } else {
        callback.certificateChosen(null, false);
      }
    }, DIALOG_RESPONSE_DELAY_MS);
  },
};

MockRegistrar.register(
  "@mozilla.org/security/ClientAuthDialogService;1",
  gClientAuthDialogService
);

function makeChan(uri, browserId) {
  let chan = NetUtil.newChannel({
    uri,
    loadUsingSystemPrincipal: true,
  }).QueryInterface(Ci.nsIHttpChannel);
  chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
  chan.browserId = browserId;
  return chan;
}

function channelOpenPromise(chan, flags) {
  return new Promise(resolve => {
    chan.asyncOpen(
      new ChannelListener((req, buffer) => resolve([req, buffer]), null, flags)
    );
  });
}

add_setup(async function setup() {
  do_get_profile();

  Services.prefs.setBoolPref("network.http.happy_eyeballs_enabled", true);
  // Shrink HE's connection-attempt-delay so the second-family racer fires
  // promptly while the first racer is still blocked on the cert dialog —
  // makes the race window observable in CI.
  Services.prefs.setIntPref(
    "network.http.happy_eyeballs_connection_attempt_delay",
    HE_CONNECTION_ATTEMPT_DELAY_MS
  );
  Services.prefs.setIntPref("network.http.speculative-parallel-limit", 0);

  // Keep client-cert discovery confined to the test pkcs11 module. Otherwise
  // on macOS the osclientcerts module queries the system keychain, which pops
  // a native OS authorization/cert-selection dialog that xpcshell can't
  // dismiss (the test would hang until timeout). This is separate from the
  // mocked nsIClientAuthDialogService.
  Services.prefs.setBoolPref("security.osclientcerts.autoload", false);

  // Load the pkcs11 test module so the cert candidate list isn't empty.
  // PSM bypasses the dialog entirely when no candidates exist. The dylib is
  // built and installed only in PSM's test working directory; reach across
  // _tests/xpcshell/ to find it.
  let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
  // CurWorkD = _tests/xpcshell/netwerk/test/unit → up to _tests/xpcshell.
  libraryFile = libraryFile.parent.parent.parent;
  libraryFile.append("security");
  libraryFile.append("manager");
  libraryFile.append("ssl");
  libraryFile.append("tests");
  libraryFile.append("unit");
  libraryFile.append("pkcs11testmodule");
  libraryFile.append(ctypes.libraryName("pkcs11testmodule"));
  await loadPKCS11Module(libraryFile, "PKCS11 Test Module");
  Cc["@mozilla.org/security/x509certdb;1"]
    .getService(Ci.nsIX509CertDB)
    .getCerts();

  gServer = new NodeHTTPSServer();
  gServer.setRequestClientCert(true);
  await gServer.start();
  await gServer.registerPathHandler("/", (_req, resp) => {
    resp.writeHead(200, { "Content-Type": "text/plain", "Content-Length": 2 });
    resp.end("OK");
  });

  registerCleanupFunction(async () => {
    Services.prefs.clearUserPref("network.http.happy_eyeballs_enabled");
    Services.prefs.clearUserPref(
      "network.http.happy_eyeballs_connection_attempt_delay"
    );
    Services.prefs.clearUserPref("network.http.speculative-parallel-limit");
    Services.prefs.clearUserPref("security.osclientcerts.autoload");
    await gServer.stop();
  });
});

add_task(async function test_he_pauses_other_racers_on_client_auth() {
  gChooseCertificateInvocations = 0;
  gChooseCertificateHostnames.length = 0;

  // Verify the host actually has two address families locally — otherwise
  // there's nothing for HE to race and the test would trivially pass.
  let resolved = await new Promise((resolve, reject) => {
    Services.dns.asyncResolve(
      "localhost",
      Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
      Ci.nsIDNSService.RESOLVE_DEFAULT_FLAGS,
      null,
      {
        QueryInterface: ChromeUtils.generateQI(["nsIDNSListener"]),
        onLookupComplete(_req, rec, status) {
          if (!Components.isSuccessCode(status)) {
            reject(status);
            return;
          }
          let addrs = [];
          rec = rec.QueryInterface(Ci.nsIDNSAddrRecord);
          while (rec.hasMore()) {
            addrs.push(rec.getNextAddrAsString());
          }
          resolve(addrs);
        },
      },
      Services.tm.currentThread,
      {}
    );
  });
  info(`localhost resolved to: ${resolved.join(", ")}`);
  Assert.greaterOrEqual(
    resolved.length,
    2,
    "localhost must have at least two addresses (likely 127.0.0.1 and ::1) " +
      "for HE to have something to race"
  );

  let chan = makeChan(`${gServer.origin()}/`, TEST_BROWSER_ID);
  let [req] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);

  Assert.equal(
    req.QueryInterface(Ci.nsIHttpChannel).responseStatus,
    200,
    "request should succeed after client cert is selected"
  );
  Assert.greaterOrEqual(
    gChooseCertificateInvocations,
    1,
    "chooseCertificate should fire at least once " +
      `(got ${gChooseCertificateInvocations}: ` +
      `${gChooseCertificateHostnames.join(", ")})`
  );
  Assert.equal(
    gChooseCertificateHostnames[0],
    "localhost",
    "dialog should be invoked for the requested hostname"
  );
});

Messung V0.5 in Prozent
C=86 H=97 G=91

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-09-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1867298