Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  http3_proxy_common.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";

/* import-globals-from head_cache.js */
/* import-globals-from head_cookies.js */
/* import-globals-from head_channels.js */
/* import-globals-from head_http3.js */

const {
  Http3ProxyFilter,
  with_node_servers,
  NodeHTTPServer,
  NodeHTTPSServer,
  NodeHTTP2Server,
  NodeHTTP2ProxyServer,
  NodeWebSocketHttp2Server,
  WebSocketConnection,
  NodeTCPEchoServer,
  NodeTLSEchoServer,
} = ChromeUtils.importESModule("resource://testing-common/NodeServer.sys.mjs");

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

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

let pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
let proxyHost;
let proxyPort;
let noResponsePort;
let proxyAuth;
let proxyFilter;

/**
 * Sets up proxy filter to MASQUE H3 proxy
 */

async function setup_http3_proxy() {
  Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
  Services.prefs.setBoolPref("network.dns.disableIPv6", true);
  Services.prefs.setIntPref("network.webtransport.datagram_size", 1500);
  Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com");
  Services.prefs.setIntPref("network.http.http3.max_gso_segments", 1); // TODO: fix underflow
  let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
    Ci.nsIX509CertDB
  );
  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
  addCertFromFile(certdb, "proxy-ca.pem", "CTu,u,u");

  proxyHost = "foo.example.com";
  ({ masqueProxyPort: proxyPort, noResponsePort } =
    await create_masque_proxy_server());
  proxyAuth = "";

  Assert.notEqual(proxyPort, null);
  Assert.notEqual(proxyPort, "");

  // A dummy request to make sure AltSvcCache::mStorage is ready.
  let chan = makeChan(`https://localhost`);
  await channelOpenPromise(chan, CL_EXPECT_FAILURE);

  proxyFilter = new Http3ProxyFilter(
    proxyHost,
    proxyPort,
    0,
    "/.well-known/masque/udp/{target_host}/{target_port}/",
    proxyAuth
  );
  pps.registerFilter(proxyFilter, 10);

  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
  });
}

/**
 * Tests HTTP connect through H3 proxy to HTTP, HTTPS and H2 servers
 * Makes multiple requests. Expects success.
 */

async function test_http_connect() {
  info("Running test_http_connect");
  await with_node_servers(
    [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server],
    async server => {
      info(`Proxying to ${server.constructor.name} server`);
      await server.registerPathHandler("/first", (req, resp) => {
        resp.writeHead(200);
        resp.end("first");
      });
      await server.registerPathHandler("/second", (req, resp) => {
        resp.writeHead(200);
        resp.end("second");
      });
      await server.registerPathHandler("/third", (req, resp) => {
        resp.writeHead(200);
        resp.end("third");
      });
      let chan = makeChan(
        `${server.protocol()}://alt1.example.com:${server.port()}/first`
      );
      let [req, buf] = await channelOpenPromise(
        chan,
        CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
      );
      Assert.equal(req.status, Cr.NS_OK);
      Assert.equal(buf, "first");
      chan = makeChan(
        `${server.protocol()}://alt1.example.com:${server.port()}/second`
      );
      [req, buf] = await channelOpenPromise(
        chan,
        CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
      );
      Assert.equal(req.status, Cr.NS_OK);
      Assert.equal(buf, "second");

      chan = makeChan(
        `${server.protocol()}://alt1.example.com:${server.port()}/third`
      );
      [req, buf] = await channelOpenPromise(
        chan,
        CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
      );
      Assert.equal(req.status, Cr.NS_OK);
      Assert.equal(buf, "third");
    }
  );
}

/**
 * Test HTTP CONNECT authentication failure - tests behavior when proxy
 * authentication is required but not provided or incorrect
 */

async function test_http_connect_auth_failure() {
  info("Running test_http_connect_auth_failure");
  await with_node_servers(
    [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server],
    async server => {
      info(`Testing auth failure with ${server.constructor.name} server`);
      // Register a handler that requires authentication
      await server.registerPathHandler("/auth-required", (req, resp) => {
        const auth = req.headers.authorization;
        if (!auth || auth !== "Basic dGVzdDp0ZXN0") {
          resp.writeHead(401, {
            "WWW-Authenticate": 'Basic realm="Test Realm"',
            "Content-Type": "text/plain",
          });
          resp.end("");
        } else {
          resp.writeHead(200);
          resp.end("Authenticated");
        }
      });

      let chan = makeChan(
        `${server.protocol()}://alt1.example.com:${server.port()}/auth-required`
      );
      let [req] = await channelOpenPromise(
        chan,
        CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
      );

      // Should receive 401 Unauthorized through the tunnel
      Assert.equal(req.status, Cr.NS_OK);
      Assert.equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 401);
    }
  );
}

/**
 * Test HTTP CONNECT with large request/response data - ensures the tunnel
 * can handle substantial data transfer without corruption or truncation
 */

async function test_http_connect_large_data() {
  info("Running test_http_connect_large_data");
  await with_node_servers(
    [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server],
    async server => {
      info(
        `Testing large data transfer with ${server.constructor.name} server`
      );
      // Create a large response payload (1MB of data)
      const largeData = "x".repeat(1024 * 1024);
      await server.registerPathHandler("/large", (req, resp) => {
        const largeData = "x".repeat(1024 * 1024);
        resp.writeHead(200, { "Content-Type": "text/plain" });
        resp.end(largeData);
      });

      let chan = makeChan(
        `${server.protocol()}://alt1.example.com:${server.port()}/large`
      );
      let [req, buf] = await channelOpenPromise(
        chan,
        CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
      );

      Assert.equal(req.status, Cr.NS_OK);
      Assert.equal(buf.length, largeData.length);
      Assert.equal(buf, largeData);
    }
  );
}

/**
 * Test HTTP CONNECT tunnel connection refused - simulates target server
 * being unreachable or refusing connections
 */

async function test_http_connect_connection_refused() {
  info("Running test_http_connect_connection_refused");
  // Test connecting to a port that's definitely not in use
  let chan = makeChan(`http://alt1.example.com:667/refused`);
  let [req] = await channelOpenPromise(chan, CL_EXPECT_FAILURE);

  // Should fail to establish tunnel connection
  Assert.notEqual(req.status, Cr.NS_OK);
  info(`Connection refused status: ${req.status}`);
}

/**
 * Test HTTP CONNECT with invalid target host - verifies proper error handling
 * when trying to tunnel to a non-existent hostname
 */

async function test_http_connect_invalid_host() {
  info("Running test_http_connect_invalid_host");
  let chan = makeChan(`http://nonexistent.invalid.example/test`);
  let [req] = await channelOpenPromise(chan, CL_EXPECT_FAILURE);

  // Should fail DNS resolution for invalid hostname
  Assert.notEqual(req.status, Cr.NS_OK);
  info(`Invalid host status: ${req.status}`);
}

/**
 * Test concurrent HTTP CONNECT tunnels - ensures multiple simultaneous
 * requests can be established and used independently through the same H3 proxy
 */

async function test_concurrent_http_connect_tunnels() {
  info("Running test_concurrent_http_connect_tunnels");
  await with_node_servers(
    [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server],
    async server => {
      info(`Testing concurrent tunnels with ${server.constructor.name} server`);

      // Register multiple endpoints
      await server.registerPathHandler("/concurrent1", (req, resp) => {
        resp.writeHead(200);
        resp.end("response1");
      });
      await server.registerPathHandler("/concurrent2", (req, resp) => {
        resp.writeHead(200);
        resp.end("response2");
      });
      await server.registerPathHandler("/concurrent3", (req, resp) => {
        resp.writeHead(200);
        resp.end("response3");
      });

      // Create multiple concurrent requests through the tunnel
      const promises = [];
      for (let i = 1; i <= 3; i++) {
        let chan = makeChan(
          `${server.protocol()}://alt1.example.com:${server.port()}/concurrent${i}`
        );
        promises.push(
          channelOpenPromise(chan, CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL)
        );
      }

      const results = await Promise.all(promises);

      // Verify all requests succeeded with correct responses
      for (let i = 0; i < 3; i++) {
        const [req, buf] = results[i];
        Assert.equal(req.status, Cr.NS_OK);
        Assert.equal(buf, `response${i + 1}`);
      }
      info("All concurrent tunnels completed successfully");
    }
  );
}

/**
 * Test HTTP CONNECT tunnel stream closure handling - verifies proper cleanup
 * when the tunnel connection is closed unexpectedly
 */

// eslint-disable-next-line no-unused-vars
async function test_http_connect_stream_closure() {
  info("Running test_http_connect_stream_closure");
  await with_node_servers([NodeHTTPServer], async server => {
    info(`Testing stream closure with ${server.constructor.name} server`);

    await server.registerPathHandler("/close", (req, resp) => {
      // Send partial response then close connection abruptly
      resp.writeHead(200, { "Content-Type": "text/plain" });
      resp.write("partial");
      // Simulate connection closure
      resp.destroy();
    });

    let chan = makeChan(
      `${server.protocol()}://alt1.example.com:${server.port()}/close`
    );
    let [req] = await channelOpenPromise(chan, CL_EXPECT_FAILURE);

    // Should handle connection closure gracefully
    Assert.notEqual(req.status, Cr.NS_OK);
    info(`Stream closure status: ${req.status}`);
  });
}

/**
 * Test connect-udp - SUCCESS case.
 * Will use h3 proxy to connect to h3 server.
 */

async function test_connect_udp() {
  info("Running test_connect_udp");
  let h3Port = Services.env.get("MOZHTTP3_PORT");
  info(`h3Port = ${h3Port}`);

  Services.prefs.setCharPref(
    "network.http.http3.alt-svc-mapping-for-testing",
    `alt1.example.com;h3=:${h3Port}`
  );

  {
    let chan = makeChan(`https://alt1.example.com:${h3Port}/no_body`);
    let [req] = await channelOpenPromise(
      chan,
      CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
    );
    Assert.equal(req.protocolVersion, "h3");
    Assert.equal(req.status, Cr.NS_OK);
    Assert.equal(req.responseStatus, 200);
  }
}

async function test_http_connect_fallback() {
  info("Running test_http_connect_fallback");
  pps.unregisterFilter(proxyFilter);

  Services.prefs.setCharPref(
    "network.http.http3.alt-svc-mapping-for-testing",
    ""
  );

  let proxyPort = noResponsePort;
  let proxy = new NodeHTTP2ProxyServer();
  await proxy.startWithoutProxyFilter(proxyPort);
  Assert.equal(proxyPort, proxy.port());
  dump(`proxy port=${proxy.port()}\n`);

  let server = new NodeHTTP2Server();
  await server.start();

  // Register multiple endpoints
  await server.registerPathHandler("/concurrent1", (req, resp) => {
    resp.writeHead(200);
    resp.end("response1");
  });
  await server.registerPathHandler("/concurrent2", (req, resp) => {
    resp.writeHead(200);
    resp.end("response2");
  });
  await server.registerPathHandler("/concurrent3", (req, resp) => {
    resp.writeHead(200);
    resp.end("response3");
  });

  let filter = new Http3ProxyFilter(
    proxyHost,
    proxy.port(),
    0,
    "/.well-known/masque/udp/{target_host}/{target_port}/",
    proxyAuth
  );
  pps.registerFilter(filter, 10);

  registerCleanupFunction(async () => {
    await proxy.stop();
    await server.stop();
  });

  // Create multiple concurrent requests through the tunnel
  const promises = [];
  for (let i = 1; i <= 3; i++) {
    let chan = makeChan(
      `${server.protocol()}://alt1.example.com:${server.port()}/concurrent${i}`
    );
    promises.push(channelOpenPromise(chan, CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL));
  }

  const results = await Promise.all(promises);

  // Verify all requests succeeded with correct responses
  for (let i = 0; i < 3; i++) {
    const [req, buf] = results[i];
    Assert.equal(req.status, Cr.NS_OK);
    Assert.equal(buf, `response${i + 1}`);
  }

  let h3Port = server.port();
  console.log(`h3Port = ${h3Port}`);

  Services.prefs.setCharPref(
    "network.http.http3.alt-svc-mapping-for-testing",
    `alt1.example.com;h3=:${h3Port}`
  );

  let chan = makeChan(`https://alt1.example.com:${h3Port}/concurrent1`);
  let [req] = await channelOpenPromise(
    chan,
    CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
  );
  Assert.equal(req.status, Cr.NS_OK);
  Assert.equal(req.responseStatus, 200);

  await proxy.stop();
  pps.unregisterFilter(filter);
  await server.stop();
}

/**
 * Helper function to open a WebSocket connection
 */

async function wsChannelOpenPromise(url, msg) {
  let conn = new WebSocketConnection();
  let statusObj = await Promise.race([conn.open(url), conn.finished()]);
  if (statusObj && statusObj.status != Cr.NS_OK) {
    return [statusObj.status, ""];
  }
  let finalStatusPromise = conn.finished();
  conn.send(msg);
  let res = await conn.receiveMessages();
  conn.close();
  let finalStatus = await finalStatusPromise;
  return [finalStatus.status, res];
}

/**
 * Test WebSocket through H3 proxy using H2 WebSocket server
 */

async function test_http_connect_websocket() {
  info("Running test_http_connect_websocket");
  Services.prefs.setBoolPref("network.http.http2.websockets", true);

  let wss = new NodeWebSocketHttp2Server();
  await wss.start();

  registerCleanupFunction(async () => {
    await wss.stop();
  });

  Assert.notEqual(wss.port(), null);
  await wss.registerMessageHandler((data, ws) => {
    ws.send(data);
  });

  let url = `wss://alt1.example.com:${wss.port()}`;
  const msg = "test h2 websocket through h3 proxy";
  let [status, res] = await wsChannelOpenPromise(url, msg);
  Assert.equal(status, Cr.NS_OK);
  Assert.deepEqual(res, [msg]);

  // Test multiple messages
  let conn = new WebSocketConnection();
  await conn.open(url);
  conn.send("message1");
  let mess1 = await conn.receiveMessages();
  Assert.deepEqual(mess1, ["message1"]);

  conn.send("message2");
  conn.send("message3");
  let mess2 = [];
  while (mess2.length < 2) {
    mess2 = mess2.concat(await conn.receiveMessages());
  }
  Assert.deepEqual(mess2, ["message2", "message3"]);

  conn.close();
  let { status: finalStatus } = await conn.finished();
  Assert.equal(finalStatus, Cr.NS_OK);

  await wss.stop();
}

async function test_inner_connection_fallback(ServerClass) {
  info("Running test_inner_connection_fallback");
  let h3Port = Services.env.get("MOZHTTP3_PORT_NO_RESPONSE");
  info(`h3Port = ${h3Port}`);

  // Register the connect-udp proxy.
  pps.registerFilter(proxyFilter, 10);

  let httpsProxy = new NodeHTTP2ProxyServer();
  await httpsProxy.startWithoutProxyFilter(proxyPort);

  let server = new ServerClass();
  await server.start(h3Port);

  // Register multiple endpoints
  await server.registerPathHandler("/concurrent1", (req, resp) => {
    resp.writeHead(200);
    resp.end("fallback1");
  });
  await server.registerPathHandler("/concurrent2", (req, resp) => {
    resp.writeHead(200);
    resp.end("fallback2");
  });
  await server.registerPathHandler("/concurrent3", (req, resp) => {
    resp.writeHead(200);
    resp.end("fallback3");
  });
  registerCleanupFunction(async () => {
    await server.stop();
    await httpsProxy.stop();
  });

  Services.prefs.setCharPref(
    "network.http.http3.alt-svc-mapping-for-testing",
    `alt1.example.com;h3=:${h3Port}`
  );

  // Create multiple concurrent requests through the tunnel
  const promises = [];
  for (let i = 1; i <= 3; i++) {
    let chan = makeChan(
      `${server.protocol()}://alt1.example.com:${h3Port}/concurrent${i}`
    );
    promises.push(channelOpenPromise(chan, CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL));
  }

  const results = await Promise.all(promises);

  // Verify all requests succeeded with correct responses
  for (let i = 0; i < 3; i++) {
    const [req, buf] = results[i];
    Assert.equal(req.status, Cr.NS_OK);
    Assert.equal(buf, `fallback${i + 1}`);
  }
  await server.stop();
  await httpsProxy.stop();

  pps.unregisterFilter(proxyFilter);
}

const CC = Components.Constructor;
const BinaryInputStream = CC(
  "@mozilla.org/binaryinputstream;1",
  "nsIBinaryInputStream",
  "setInputStream"
);
const BinaryOutputStream = CC(
  "@mozilla.org/binaryoutputstream;1",
  "nsIBinaryOutputStream",
  "setOutputStream"
);

function createUpgradeHandlers({ writePayload = "hello" } = {}) {
  const handler = {
    _resolve: null,

    waitForData() {
      return new Promise(resolve => {
        this._resolve = resolve;
      });
    },

    onInputStreamReady(input) {
      try {
        const bis = new BinaryInputStream(input);
        const n = input.available();
        if (n > 0) {
          const data = bis.readByteArray(n);
          if (this._resolve) {
            this._resolve(data);
            this._resolve = null;
          }
        }
      } catch (e) {
        Assert.ok(false, `onInputStreamReady threw: ${e}`);
      }
    },

    onOutputStreamReady(output) {
      try {
        const bos = new BinaryOutputStream(output);
        bos.writeByteArray(new TextEncoder().encode(writePayload));
      } catch (e) {
        Assert.ok(false, `onOutputStreamReady threw: ${e}`);
      }
    },

    QueryInterface: ChromeUtils.generateQI([
      "nsIInputStreamCallback",
      "nsIOutputStreamCallback",
    ]),
  };

  const listener = {
    onTransportAvailable(transport, socketIn, socketOut) {
      Assert.ok(
        transport && socketIn && socketOut,
        "transport and streams present"
      );
      socketIn.asyncWait(handler, 0, 0, Services.tm.mainThread);
      socketOut.asyncWait(handler, 0, 0, Services.tm.mainThread);
    },
    QueryInterface: ChromeUtils.generateQI(["nsIHttpUpgradeListener"]),
  };

  return { upgradeListener: listener, connectHandler: handler };
}

async function test_http_connect_only() {
  info("Running test_http_connect_only");
  Services.prefs.setCharPref(
    "network.http.http3.alt-svc-mapping-for-testing",
    ""
  );

  let echo = new NodeTCPEchoServer();
  await echo.start();
  registerCleanupFunction(async () => {
    await echo.stop();
  });

  pps.registerFilter(proxyFilter, 10);

  const TEST_DATA = "HelloWorld";
  const { upgradeListener, connectHandler } = createUpgradeHandlers({
    writePayload: TEST_DATA,
  });

  const dataPromise = connectHandler.waitForData();

  let chan = makeChan(`http://localhost:${echo.port()}/`);
  let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
  internal.HTTPUpgrade("webrtc", upgradeListener);
  internal.setConnectOnly(true);

  let [req] = await channelOpenPromise(
    chan,
    CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
  );

  Assert.equal(req.status, Cr.NS_OK);
  Assert.equal(req.responseStatus, 200);

  const data = await dataPromise;
  Assert.equal(TEST_DATA, String.fromCharCode.apply(String, data));
}

async function test_https_connect_only() {
  info("Running test_http_connect_only");
  pps.unregisterFilter(proxyFilter);

  let echo = new NodeTLSEchoServer();
  await echo.start();
  registerCleanupFunction(async () => {
    await echo.stop();
  });

  pps.registerFilter(proxyFilter, 10);

  const TEST_DATA = "HelloWorld";
  const { upgradeListener, connectHandler } = createUpgradeHandlers({
    writePayload: TEST_DATA,
  });

  const dataPromise = connectHandler.waitForData();

  let chan = makeChan(`https://localhost:${echo.port()}/`);
  let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
  internal.HTTPUpgrade("webrtc", upgradeListener);
  internal.setConnectOnly(true);

  let [req] = await channelOpenPromise(
    chan,
    CL_IGNORE_CL | CL_ALLOW_UNKNOWN_CL
  );

  Assert.equal(req.status, Cr.NS_OK);
  Assert.equal(req.responseStatus, 200);

  const data = await dataPromise;
  Assert.equal(TEST_DATA, String.fromCharCode.apply(String, data));
}

Messung V0.5 in Prozent
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-10-04) ¤

*© 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